- The full module graph contains HashMaps (of the currently loaded packages, etc) which are backed by Object arrays.
- Initially, all of these backing arrays are in the Open Region.
- When new classes are loaded, these HashMaps are updated
- As a result, a non-archived object may be stored in to an archived backing array ARR.
- i.e., Object OBJ = new Object(); ARR[n] = OBJ;
- Under some circumstances, ARR becomes garbage (e.g., when the HashMap is expanded)
- ARR is now a Type B object.
- Later, a GC happens, and OBJ is relocated due to GC
- However, ARR[n] is not relocated (because ARR is not reachable??). As a result, ARR[n] becomes invalid (points to garbage)
- Under some circumstances (GC verification – and possibly other situations as well??), ARR[n] is dereferenced and we would get an assert or crash in the GC.
Root List - Distinguishing Live and Dead Objects in Open Region
In JDK 16, we will implement a Root List in the Archived Heap to allow the GC to reliably determine whether an archived object is alive or dead:
- Prototype: http://cr.openjdk.java.net/~iklam/jdk16/8253081-open-archive-root-list/
- Start at HeapShared::roots(). This is where the Root List is implemented
At VM bootstrap, the Root List contains all objects in the Open Region that may be referenced in the future, including
- mirrors of classes that have not yet been loaded.
- ConstantPool::resolved_references() of classes that have not yet been loaded.
- Objects in ArchivedKlassSubGraphInfoRecords that have not been initialized.
- java.lang.Module objects that have not yet been added to the module graph.
When a mirror M becomes referenced by a newly loaded class K, M will be removed from the Root List, and K will be responsible for
keeping M alive.
Other types of roots are also similarly removed from the Root List when they become referenced.
The Root List is implemented as a ObjArray stored inside a global OopHandle, so it will always be scanned by the GC. Therefore, the GC can use reachability to tell whether an object in the Open Region is live or dead. In fact, the Open Region now becomes just like other regular G1 regions. We should be able to remove most of G1's special handling code for the Open Region.
In the example above:
- At VM bootstrap, the ARR array is referenced indirectly by an ArchivedKlassSubGraphInfoRecord, whose root object is stored in the Root List, so it's reachable.
- Later, when the ArchivedKlassSubGraphInfoRecord is initialized, its root object is removed from the Root List and stored into Java static field (e.g., ArchivedBootLayer. archivedBootLayer), and ARR will be indirectly referenced from there.
- Later, when ARR is discarded by the HashMap due to table expansion, it is no longer reachable. G1 knows that ARR is dead and will never dereference ARR[n].