JJUG CCC 2017 Fallで一番楽しみにしていたセッション(そして素晴らしいセッションでした)"CPUから見たG1GC"で、初めて知ったことがありました。
www.slideshare.net
JITコンパイルのコードの中に、使っているGCアルゴリズムでの分岐があるということです。
void GraphKit::post_barrier(Node* ctl, Node* store, Node* obj, Node* adr, uint adr_idx, Node* val, BasicType bt, bool use_precise) { BarrierSet* bs = Universe::heap()->barrier_set(); set_control(ctl); switch (bs->kind()) { case BarrierSet::G1SATBCTLogging: g1_write_barrier_post(store, obj, adr, adr_idx, val, bt, use_precise); break; case BarrierSet::CardTableForRS: case BarrierSet::CardTableExtension: write_barrier_post(store, obj, adr, adr_idx, val, use_precise); break; case BarrierSet::ModRef: break; default : ShouldNotReachHere(); } }
src/hotspot/share/opto/graphKit.cpp です。caseでG1GCだったらという分岐があります。
Graalにもあるだろうと考え、調べてみました。"g1gc"で検索すると、org.graalvm.compiler.hotspot.phases.WriteBarrierAdditionPhase
とWriteBarrierVerificationPhase
が見つかりました。
WriteBarrierVerificationPhase
のコードです。
private void validateWrite(Node write) { ... NodeFlood frontier = write.graph().createNodeFlood(); expandFrontier(frontier, write); Iterator<Node> iterator = frontier.iterator(); while (iterator.hasNext()) { Node currentNode = iterator.next(); if (isSafepoint(currentNode)) { throw new AssertionError("Write barrier must be present " + write.toString(Verbosity.All) + " / " + write.inputs()); } if (useG1GC()) { if (!(currentNode instanceof G1PostWriteBarrier) || (!validateBarrier((FixedAccessNode) write, (ObjectWriteBarrier) currentNode))) { expandFrontier(frontier, currentNode); } } else { if (!(currentNode instanceof SerialWriteBarrier) || (!validateBarrier((FixedAccessNode) write, (ObjectWriteBarrier) currentNode)) || ((currentNode instanceof SerialWriteBarrier) && !validateBarrier((FixedAccessNode) write, (ObjectWriteBarrier) currentNode))) { expandFrontier(frontier, currentNode); } } } } private boolean useG1GC() { return config.useG1GC; }
WriteBarrierAdditionPhase
のコードです。
private void addWriteNodeBarriers(WriteNode node, StructuredGraph graph) { BarrierType barrierType = node.getBarrierType(); switch (barrierType) { case NONE: // nothing to do break; case IMPRECISE: case PRECISE: boolean precise = barrierType == BarrierType.PRECISE; if (config.useG1GC) { if (!node.getLocationIdentity().isInit()) { addG1PreWriteBarrier(node, node.getAddress(), null, true, node.getNullCheck(), graph); } addG1PostWriteBarrier(node, node.getAddress(), node.value(), precise, graph); } else { addSerialPostWriteBarrier(node, node.getAddress(), node.value(), precise, graph); } break; default: throw new GraalError("unexpected barrier type: " + barrierType); } }
どちらもライトバリアに関する処理をするPhaseです。G1GCではポストライトバリアでリメンバーセットを更新します。
G1 GCでは、個別の記憶集合(RSet)を使用して、リージョンへの参照を追跡します。個別のRSetを使用すると、そのリージョンへの参照のスキャンが必要になるのはヒープ全体ではなくリージョンのRSetだけになるので、リージョンのコレクションを並列かつ個別に実行できるようになります。G1 GCでは、ポストライト・バリアを使用してヒープの変更を記録し、RSetを更新します。 https://docs.oracle.com/javase/jp/8/docs/technotes/guides/vm/gctuning/g1_gc_tuning.html
RSet = リメンバーセットです。G1PostWriteBarrier
クラスもValueNode
クラスのサブクラスであり、Graalのグラフに表現されるようです。で、なぜそうしているのか?、どういう処理をしているのか?について、僕はわかりません。
G1PostWriteBarrier
オブジェクトを実際に処理しているのはorg.graalvm.compiler.hotspot.replacements.WriteBarrierSnippets
ですが、
public void lower(G1PostWriteBarrier writeBarrierPost, HotSpotRegistersProvider registers, LoweringTool tool) { StructuredGraph graph = writeBarrierPost.graph(); if (writeBarrierPost.alwaysNull()) { graph.removeFixed(writeBarrierPost); return; } Arguments args = new Arguments(g1PostWriteBarrier, graph.getGuardsStage(), tool.getLoweringStage()); AddressNode address = writeBarrierPost.getAddress(); args.add("address", address); if (address instanceof OffsetAddressNode) { args.add("object", ((OffsetAddressNode) address).getBase()); } else { assert writeBarrierPost.usePrecise() : "found imprecise barrier that's not an object access " + writeBarrierPost; args.add("object", null); } ValueNode value = writeBarrierPost.getValue(); if (value.stamp() instanceof NarrowOopStamp) { assert oopEncoding != null; value = HotSpotCompressionNode.uncompress(value, oopEncoding); } args.add("value", value); args.addConst("usePrecise", writeBarrierPost.usePrecise()); args.addConst("threadRegister", registers.getThreadRegister()); args.addConst("trace", traceBarrier(writeBarrierPost.graph())); args.addConst("counters", counters); template(graph.getDebug(), args).instantiate(providers.getMetaAccess(), writeBarrierPost, DEFAULT_REPLACER, args); }
何をしているのかわかりませんでした。