Java Allocation and C2
Java object allocation and escape analysis play crucial roles in memory management and performance optimization within the Java Virtual Machine (JVM). This comprehensive overview covers topics such as object vs. scalar allocation, object allocation mechanisms, hotspot escape analysis, ideal and connection graphs, and points-to relationships.
Download Presentation
Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
E N D
Presentation Transcript
Java Allocation and C2 Escape Analysis xxinliu@JDKTeam
Object and scalar in Java Scalars are ALL on JVM Operand stack (JVM Spec 2.6.2) 8 primitives: short, int,long,float,double, byte,char, Boolean Reference: points to an object or its field. Objects are on Java Heap (runtime managed memory), aka. oop Each Object has a header Object Array T[]
Allocation in Java Who is allocating object? Where? Keyword new in java new Object() => bytecode new new int[] => bytecode newarray Variants: anewarray, multianewarray Autoboxing => int java.lang.Integer java.lang.Object.clone() -> Object java.util.Arrays.copyOf() -> T[] Reflection : java.lang.reflection.Constructor.newInstance() Native code can allocate objects JVM itself can allocate internal objects.
Allocation in Java Bottom line: it s hard to keep tracking all object allocation. JFR is an low-cost proximate approach ObjectAllocationInNewTLAB ObjectAllocationOutsideTLAB
HotSpot Escape Analysis bcEscapeAnalyzer Inter-procedural Input: method in bytecode Output: method data(MDO) C2 EA intra-procedural Input: compilation unit(after inlining) in ideal graph Intermediate: connection graph Output: annotation of Allocations nodes class AllocationNode { ... // Result of Escape Analysis bool _is_scalar_replacement; bool _is_non_escaping; ... };
Connection Graph Nodes: Java Object(JO), only JOs are on-heap! Local Variable(LV) Field of an Object(FO) Edges Result: for each JO node Escape? {No, Arg, Global} NSR: Non-Scalar-Replaceable?
Escape Analysis by an example Example Edges: points-to -XX:+PrintEscapeAnalysis Uses: nodes which point to this JO s points-to set {91F, 88F} {38, 43} point-to JO, or uses
Ideal graph & Connection graph Connection Graph Ideal Graph Points-to Java Object LV: o Points-to FO: x Points-to relationship: 2 variables p & q, p points-to q if p and q may refer to the same memory location.
Scalar Replacement Is not putting an object entirely with header on java stack It works as if program break down an instance into scalars Happens in MacroExpand phase. Macro nodes: LockNode, AllocateNode, AllocateArrayNode, ArrayCopyNode
Scalar Replacement Outline Precondition Non-escaping Unique-typing Scalar replacement: boils down to disambiguate all individual memories of fields and rewire to the scalar value Memory disambiguation Postcondition Emits information to the debug info section Used to reallocate and initialize the objects in deoptimization
Scalar Replacement: Precondition AllocateNode::_is_non_escaping is true Not NSR or autoboxing node or dead object
Part-2 Mainly talking about non-escaping but NSR objects Recap Points-to analysis Unique typing: a java object is exclusively used by all Local variables. a few reasons to mark an object NSR merging nodes hinder unique typing. Induction variable as the index of array
Non-escaping but NSR trivial NSR due to aliasing Unique typing! Java Object: a LV: o Points-to Java Object: a Java Object: b Points-to Points-to FO: x LV: o o.x => x o.x => phi(0, x), but it doesn t work in deoptimization
Close-up: a merging point Java Object: a Java Object: b Points-to Points-to LV: o 28 Allocate is pointed by {40, 45, 200}. Among them, 200 points to {28P, 115P}. because 200, both 28 and 115 are not unique typing!
Why unique typing matters for SR? We can scalarize a non-unique-typing object using phi anyway, but JVM assumes all objects live on Java Heap => JVM maintains local variables on frames, which points-to java heap. Once out of a nmethod, we have to keep this assumption(illusion?). How does it work? SR: If a local variable is live at a safepoint, create a SafePointScalarObject which collects field values. (unique typing is required here) Codegen: emit DebugInfo to codecache for each SafePointScalarObject. Deoptimization: use debug info to reallocate those objects and initialize them.
How C2 enforces unique typing? Def: A java object is exclusively used by all Local variables. ??,??????_????_??????(lv) ConnectionGraph::split_unique_types() Phase 1: refine allocate s adr_type to an instance-specific TypeOopPtr, and AddP, Phi, Cast etc. Phase 2~3: redefine mem nodes(load/store, mergemem, memphi) with the new alias
Ivanov/Divinos splitting approach Introducing a selector to decouple a merging point. B0: o1 = new MyPair(0, 0); B0: o1 = new MyPair(0, 0); If cond B1: o2 = new MyPair(x, y); B2: o2 = new MyPair(x, y); B2: selector = PHI(o1, o2); Cmp selector, 0 B2: o3 = PHI(o1, o2); Load o3.x B3 x1 = o1.x B4 x2 = o2.x B5: x3 = phi(x1, x2)
Memory Disambiguation Alias Analysis Java programs still have aliases. They are strong-typed. Type-based Alias Analysis(TBAA) Alias == memslice == adr_type(mapped) C2 MemSSA Always maintain ssa-form for memories. Nodes with memory side effect: TypePtr * adr_type() <=> alias_type. Some PHIs are memory PHI. Phi->type() is Memory, aka. yellow phi A MergeMemNode bookkeeps alias state. Unique typing: distinct an instance from the general type
Close-up: a merging point 2 distinct memory locations 28 Allocate JO(3) 115 Allocate JO(4) converge at 200 PHI & 198 PHI
My idea: splitting for better aliases Let s refocus on MergeMem, merge->in(6) == 198 PHI LoadI<6> contains 2 mem locations Splitting for more precise AA Split_through_phi exists In LoadNode boxing objs In LoopOpts
Split_through_phi alias<6> splits into alias<X> and alias<X+1>!
SR in a special case recap: If a local variable is live at a safepoint, create a SafePointScalarObject which collects its field values. Sometimes an object has been dead before reaching any safepoint A safepoint is inserted here at exit In this case, o is dead at the exit block, so both 2 objects have been dead at the safepoint. Therefore, we can proceed to SR even they are not unique typing .