-
Notifications
You must be signed in to change notification settings - Fork 79
Description
In 24 September 2025, we discussed the difference between "barrier policies" and "barrier implementations".
A barrier mechanism, in simple terms, can be an object-logging write barrier, a field-logging write barrier, a read barrier for reading weak fields, etc, and it can be pre-barriers or post-barriers.
On the other hand, a barrier policy is a combination of barrier implementations. For example, to implement the SATB-based ConcurrentImmix plan, we need the VM to implement either the object-logging pre-write barrier that records all fields, or the field-logging pre-write barrier that records the single modified field, and we also need the VM to apply a read barrier when reading from weak fields.
Status quo
Currently, implementations of trait Barrier are actually barrier policies. For example, SATBBarrier combines both a load barrier and a pre-write barrier, implemented as different methods of SATBBarrier. Its pre-write barrier currently has object granularity.
If we add LXR, it will need to combine three barrier mechanisms.
Granularity of barrier mechanisms
A barrier mechanism can operate on a granularity
- whole object (e.g. object-logging barrier)
- single field (e.g. field-logging barrier)
- a slice
What operations to apply?
Depending on VMs, there are different operations to apply barriers to. For example,
- Writing to a scalar field (
astorein JVM) - Writing to an array element (
aastorein JVM) - Bulk clearing/setting/copying array elements (arraycopy in JVM)
- Reading from a weak reference filed (
Reference.get()exceptPhantomReference.get()which always returnsnull).
One key aspect of separating mechanisms from policies is that we can apply different mechanisms to different operations.
For example, in OpenJDK, field accesses like obj.field = val usually have access to the obj address, so we can use either object-logging or field-logging barriers to implement the SATB barrier and the generational remembered set barrier (the so-called ObjectBarrier); but arraycopy does not give the JIT compiler the base address, so we have to use field-grained or slice-grained barrier mechanisms that does not depend on the base address.
- OpenJDK
- Object fields: object-grained or field-grained
- Single array element: object-grained or field-grained
- Arraycopy: slice-grained
As another example, CRuby gives the interpreter field addresses and array element addresses for T_OBJECT ("ordinary" Ruby objects) and T_ARRAY (Ruby arrays), so we can use field-grained barriers for those types. However, the vast majority of other types are implemented in C, and some C code accesses objects (including T_OBJECT and T_ARRAY) using built-in C functions. Sometimes the C code just modifies some fields and calls rb_gc_writebarrier_remember(obj) on the whole object. We have to use object-grained barriers for those types.
- CRuby
- Accessing
T_OBJECTfields andT_ARRAYelements from Ruby: object-grained or field-grained - Custom types implemented in C, or field accesses in C without field addresses: object-grained
- Accessing
Implications for plans
Most plans that need barriers can work with either object-grained or field/slice-grained mechanisms. It has two implications:
- Plans need to be implemented in a general way so that VMs can choose to use object-grained or field-grained barriers.
- They need a specification so that VM binding developers know which barrier mechanisms to implement in the fast paths, such as SATB barriers or object-remembering (remembered set) barriers.