RFR[S] 8210289 ArchivedKlassSubGraphInfoRecord is incomplete
ioi.lam at oracle.com
Sun Sep 2 03:58:47 UTC 2018
Thanks for looking at the optimization of this problem. I think your
algorithm will run into problems if (a) there is a cycle, and (b) two
sub-graphs point to different members in the cycle.
Maybe someone familiar with graph theory can suggest a solution?
My current thinking is to first find all the cycles in the archived
heap. The reachability of every member in the cycle is the same, so we
can collapse each cycle into a single node. After that we have a DAG.
Then, instead of encoding the class initialization order as multiple
lists, we can encode it as a DAG to save space.
In any case, I think this is a sufficiently difficult problem, so we
should separate the dumping of the sub-graphs (the first part of
HeapShared::archive_module_graph_objects() in my patch) and the
computation of the initialization order
Also, I think we should defer the optimization of
record_subgraph_klasses in a future RFE. That way, we can have
correctness now, and performance later when we actually need it. (The
current implementation of record_subgraph_klasses() has not caused any
perceivable delay for the current set of objects that we are archiving).
On 9/1/18 7:55 PM, Jiangli Zhou wrote:
>> On Sep 1, 2018, at 7:25 PM, Jiangli Zhou <jiangli.zhou at oracle.com> wrote:
>> Hi Ioi,
>> Thanks for finding the bug. To address the incomplete class list issue, the original algorithm can be augmented while remaining as an O(N) solution.
>> As each node (object) in an archived subgraph is a root of a sub-subgraph, the class-initialization-list associated with a specific node (within an existing archived sub-graph) is also a sub-list of the enclosing sub-graph's class-initialization-list. For example,
>> / \
>> O2(k2) O5(k5)
>> / \
>> O3(k3) O4(k4)
>> Klass-init-list: K1, K2, K3, K4, K5
>> / \
>> O3(k3) O4(k4)
>> Klass-init-list: K2, K3, K4
>> During the sub-graph walking and archiving process for sub-graph2, if O2 has been previously archived in another sub-graph, we can find the other sub-graph's class list and take the sub-list starting from the klass (k2), without re-walking each nodes again. To do that, we only need to walk the existing recorded class-initialization-list. If K2 is found in any of the existing list, we can take the sub-list starting from K2 and append the list to the current one.
>> With the approach, K5 would also be included if O5 is walked after O2 in sub-graph1. However, O5 is not part of O2's sub-graph. So that would introduce overhead at runtime. To avoid such problem, we can remember the sub-graph level (which is already built as part of the existing graph walking algorithm) for each K in the class-initialization-list. The ending indication of the sub-list would be the first K with the same level as the starting K. So we would have:
>> K1(1), K2(2), K3(3), K4(4), K5(2)
>> The K2 level is 2, the sub-list would end before K2 who's level is 2.
> Typo. The second K2 above should be k5:
> The K2 level is 2, the sub-list would end before K5, who's level is 2.
>> This part is not currently implemented yet but is not difficult to add.
>>> On 9/1/18 6:08 PM, Ioi Lam wrote:
>>> I found this bug while trying to merge my code for CDS support of Lambda
>>> classes (JDK-8198698).
>>> When heapShared.cpp dumps a sub-graph of heap objects, it attempts to
>>> record all the classes of all the objects that are referenced by
>>> this sub-graph. However, if one of these objects have already been visited
>>> while a previous sub-graph was dumped, then this object's class is not
>>> recorded in the current sub-graph.
>>> At runtime, if the current sub-graph is restored before any other
>>> sub-graphs, we will end up with a live object in the Java heap with
>>> an uninitialized class.
>>> Now I create the sub-graph's klasses list after all sub-graphs have dumped.
>>> For each class that has archived sub-graph(s), I do a heap walk to discover
>>> all klasses that are reachable from this archived fields of this class.
>>> This is sub-optimal but suffice for now because we just have a very small
>>> number of sub-graphs. The worst case its O(N^2) where N is the number of
>>> objects in the archived heap.
>>> In the future, we might need to implement a more efficient algorithm that
>>> walks the heap once and computes all the klasses lists of all the
>>> sub-graphs at the same time.
>>> - Ioi
More information about the hotspot-runtime-dev