问题描述
public static void main(){
LargeObject lo = new LargeObject();
lo.doSomeThing();
while(true){
whatever();
}
}
lo 指向的对象能够在在循环过程中被回收吗?
前面的代码人工优化的结果是下面两种,编译器或VM能够实现这样的优化么?什么样的编译器优化技术能够实现?
public static void main(){
LargeObject lo = new LargeObject();
lo.doSomeThing();
lo = null;
while(true){
whatever();
}
}
public static void main(){
{
LargeObject lo = new LargeObject();
lo.doSomeThing();
}
while(true){
whatever();
}
}
以前写过一些,请参考传送门:
答复: HotSpot VM 内存堆的两个Survivor区下面稍微新写点字吧。不知道有多少人能耐心读完,反正我先写着 >_<
===================================================
简单回答题主的问题:
lo 指向的对象能够在在循环过程中被回收吗?
能。JVM规范没有说不能,所以具体实现有选择的自由。主流JVM在做完从Java字节码到机器码的编译后,都能做适当的优化来让题主例中的'lo'变量所指向的对象可以被回收。
做到这种效果的编译优化技术叫做“活跃分析”(liveness analysis)。一个变量只有被使用的地方才是“活跃”的;如果没有(继续)被使用,那么一个变量就“死”掉了。例子请参考这个传送门:
如果变量在后面的代码中不再被引用, 在生存期内, 它的寄存器可以被编译器挪为他用吗? - RednaxelaFX 的回答活跃分析是一种any-path backward data-flow analysis——信息是从后向前传播的,变量只要在任意路径上是活的那就是活的。
那么JVM的(JIT)编译器在做了活跃分析优化之后,是如何让GC知道某个引用类型的局部变量已经不重要了呢?很简单,通过一种叫做“GC map”的数据结构。请参考这个传送门:
找出栈上的指针/引用以HotSpot VM为具体例子看题主的代码例子。HotSpot VM里,解释执行的方法可以在任意字节码边界上进入GC,但JIT编译后的代码并不能在“任意位置”进入GC。可以进入GC的“特定位置”叫做“GC safepoint”,或者简称“safepoint”。这些位置是:
- 主动safepoint:由方法里的代码通过主动轮询去发现需要进入safepoint。有两种情况:
- 循环回跳处(loop backedge)
- 临返回处(return)
- 被动safepoint:调用别的方法的调用点。之所以叫做“被动”是因为并不是该方法主动发现要进入safepoint的,而是某个被调用的方法主动进入了safepoint,导致其整条调用链上的调用者都被动的进入了safepoint。
回头看题主的例子,假设代码里的调用点都没有被内联的话:
public static void main() {
LargeObject lo = new LargeObject(); // safepoint 1/2: 被动safepoint
lo.doSomeThing(); // safepoint 3: 被动safepoint
while (true) {
whatever(); // safepoint 4: 被动safepoint
// 由于循环体里面有未内联的方法调用,也就是说已经有被动safepoint,HotSpot会优化掉循环回跳位置的主动safepoint。假设没有被优化掉的话,此处会有:
// safepoint 5: 主动safepoint:循环回跳轮询(backedge poll)
}
// 上面循环是无限循环,所以下面如果有代码都属于不可到达的代码(unreachable code)
// 如果上面的循环不是无限循环的话,则会有:
// safepoint 6: 主动safepoint:返回前轮询(return poll)
}
简单讲解这6个safepoint分别是什么:
- safepoint 1是由new带来的safepoint。new字节码要做的事情是给新对象分配空间,在HotSpot VM的JIT编译过的代码里,分配空间分快速路径和慢速路径两边:
- 快速路径是通过TLAB分配,整个快速路径内联在JIT编译的代码里,不需要做额外的方法调用。如果能成功从TLAB分配到足够空间,则执行完快速路径就好了,否则会进一步进入慢速路径。
- 慢速路径会调用到JVM runtime里,做若干不同尝试来分配空间。这会做一个VM runtime call,有一个函数调用——safepoint 1就在这个调用的位置上。VM里实现慢速路径的逻辑会尝试分配新的TLAB,或者触发GC清理掉无用对象后再尝试分配空间。由于做GC的决定并不是main()方法自己发现,而是调用进VM runtime里由JVM决定的,所以这个safepoint算被动而不是主动safepoint。
- safepoint 2是由构造器调用的invokespecial带来的被动safepoint。要留意,Java语言层面的new Object()在字节码里是两条指令,new和invokespecial——new只负责分配空间和做默认初始化,invokespecial才会调用构造器。请参考传送门:实例构造器是不是静态方法?
- safepoint 3、4都是方法调用的被动safepoint。跟safepoint 2一样,都是调用别的Java方法的调用点上有可能被动进入safepoint。例如说,如果main()方法本身在主动执行的时候并没有发现要进safepoint,而在lo.doSomeThing()执行的时候后者主动发现要进入safepoint的话,那调用者main()也必须进入safepoint,允许GC扫描其栈帧里的引用类型的局部变量。
- safepoint 5是在循环末尾要跳回到循环开头处(backedge)的主动safepoint。这里HotSpot VM会生成代码检测VM是否发出了通知说要进入GC了,如果有通知的话就会在这个位置进入GC。
- safepoint 6是在临返回前(return)的主动safepoint。跟上一个一样,也会有代码主动去检测是否要GC。
再多废话几句:大家都知道System.gc()可以让Java代码主动触发GC,但从HotSpot VM的角度看,讨论safepoint与OopMap时,System.gc()的调用点其实是个“被动safepoint”。
专门指出这个是为了避免读者误会这里说的主动/被动。强调一下,这里说的:
- 主动:有显式代码轮询是否要GC;
- 被动:在一个未内联的调用点,为被调用方法可能进入GC而做好准备,调用方并没有主动轮询是否要GC的代码。
既然只有这些safepoint的地方可能进入GC,就JVM只需要在这些地方提高足够信息让GC知道栈帧里什么位置有引用类型的局部变量。在HotSpot VM里,这种“信息”——前面说的“GC map”——通过名为OopMap的数据结构记录。关于OopMap具体是个怎样的东西,请参考前面提到过的传送门:
找出栈上的指针/引用。
回到题主的例子,6个safepoint对应的OopMap信息分别会是:
public static void main() {
LargeObject lo = new LargeObject(); // safepoint 1/2: 被动safepoint
// OopMap 1: 空。
// 该栈帧里尚未有任何引用类型的局部变量是活跃的——new还没执行完
// OopMap 2: 记录了一个变量:刚分配的对象的引用是活的,在OopMap里;不过这还不是局部变量lo而是求值栈上的一个slot
lo.doSomeThing(); // safepoint 3: 被动safepoint
// OopMap 3: 空。局部变量lo的最后一次使用是作为上面方法的"this"参数传递出去;
// 维持"this"的存活是被调用方法的责任而不是调用方法的责任。此后局部变量lo再也没有被使用过,所以对main()来说lo在此处已死。
while (true) {
whatever(); // safepoint 4: 被动safepoint
// OopMap 4: 空。
// safepoint 5: 主动safepoint:循环回跳轮询(backedge poll)
// OopMap 5: 空。
}
// 上面循环是无限循环,所以下面如果有代码都属于不可到达的代码(unreachable code)
// 如果上面的循环不是无限循环的话,则:
// safepoint 6: 主动safepoint:返回前轮询(return poll)
// OopMap 6: 空
}
这样,编译器通过活跃分析得知lo具体被使用的范围,就可以向OopMap填入相应的信息,让局部变量'lo'只在需要存活的时候被GC扫描到——只要OopMap没有记录它的存在,GC就不会扫描它,而它所引用的对象就不会因为'lo'这个局部变量而被认为是活的,因而有机会被回收(假如没有其它活引用继续指向那个LargeObject对象的话。)
为了证明以上描述不是忽悠,下面给出HotSpot VM在上例各safepoint具体计算出的OopMap情况。测试代码、具体方法和完整日志附在本回答的最后,仅供参考。
safepoint 1:
087 call,static wrapper for: _new_instance_Java
# LargeObject::main @ bci:0 L[0]=_
# OopMap{off=140}
这里L[0]=_的意思是局部变量区slot 0还没有有效值(以'_'表示)。
OopMap里没有记录任何Java代码中能看到的引用,所以上面描述为“空”是正确的。off=140指的是该OopMap关联的指令在生成代码中的相对方法起始地址的偏移量。
safepoint 2:
05b call,static LargeObject::<init>
# LargeObject::main @ bci:4 L[0]=_ STK[0]=RBP
# OopMap{rbp=Oop off=96}
这里L[0]仍然为'_',因为刚创建的对象的引用尚未赋值给局部变量'lo'。但是有一个活的引用需要记录在OopMap里:STK[0]是求值栈的栈顶,此时持有新创建的对象的引用。
OopMap里记录下了rbp=Oop,就是说rbp寄存器此时持有一个GC需要扫描的对象引用。
safepoint 3:
063 call,static LargeObject::doSomeThing
# LargeObject::main @ bci:9 L[0]=_
# OopMap{off=104}
不用多解释了。对JIT编译器来说,局部变量'lo'的生命期到此已结束,因而L[0]又是'_'。OopMap又是空的。
safepoint 4:
073 call,static LargeObject::whatever
# LargeObject::main @ bci:12 L[0]=_
# OopMap{off=120}
跟上一个safepoint一样。
safepoint 5和6实际并不存在,所以也没有对应的日志。
===================================================
上面的描述跟题主后面举的两种“优化”形式的代码例子其实都不一样。
public static void main() { LargeObject lo = new LargeObject(); lo.doSomeThing(); lo = null; while (true) { whatever(); } }
HotSpot VM的JIT编译器做的优化,硬要说的话效果跟这个类似。这个是显式把局部变量'lo'置为null从而切断其对LargeObject对象的引用,而实际发生的状况是JIT编译器在doSomething()调用之后就不把局部变量'lo'包含在OopMap里了,于是GC根本看不到这个变量,也不关心它引用了谁,自然的切断了引用。
这个显式置null版本的Java代码,在实际运行中能够可靠的切断局部变量'lo'对对象的引用,其实有时候还是可以推荐用的——虽然它对JIT编译的代码不会有任何额外好处——但并不是所有Java方法都会被JIT编译。如果有个Java方法没有被JIT编译但里面仍然有代码会执行比较长时间,那么在那段会执行长时间的代码前显式将不需要的引用类型局部变量置null是可取的,可以谨慎使用。
===================================================
而后面一个版本:
public static void main() { { LargeObject lo = new LargeObject(); lo.doSomeThing(); } while (true) { whatever(); } }
从Java语言层面的作用域看似乎跟前面说的“活跃分析”达到了一样的效果——限制了局部变量'lo'的可见范围。但在实际执行的层面上这里却有个陷阱:
JVM规范并没有对GC的行为、JVM与GC的交互做多少规定,也没有讨论过局部变量在离开作用域之后JVM要如何处理它。这些都给JVM的实现留下了自由度——换句话说,JVM实现并没有义务在一个引用类型的局部变量离开作用域之后把它置为null。
这就允许了HotSpot VM在解释模式里有略为奇怪的表现。请看下面例子:
public class TestGC {
public static void main(String[] args) {
{
// 'o' in local variable slot 1
Object o = new byte[4*1024*1024];
o.hashCode();
}
// o is out of scope now, but...
System.gc(); // the 4M byte[] is kept alive in this GC
{
// 'i' in local variable slot 1
int i = 0; // "null out" local variable slot 1
}
System.gc(); // the 4M byte[] is freed in this GC
}
}
用Oracle JDK8来运行它:
$ java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
Java HotSpot(TM) 64-Bit Server VM (build 25.0-b70, mixed mode)
$ java -XX:+PrintGCDetails -XX:+UseSerialGC -Xmx20m -Xmn10m TestGC
[Full GC (System.gc()) [Tenured: 0K->4530K(10240K), 0.0041590 secs] 5098K->4530K(19456K), [Metaspace: 2716K->2716K(1056768K)], 0.0041940 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [Tenured: 4530K->434K(10240K), 0.0014000 secs] 4530K->434K(19456K), [Metaspace: 2716K->2716K(1056768K)], 0.0014180 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
Heap
def new generation total 9216K, used 246K [0x00000007bec00000, 0x00000007bf600000, 0x00000007bf600000)
eden space 8192K, 3% used [0x00000007bec00000, 0x00000007bec3d8e0, 0x00000007bf400000)
from space 1024K, 0% used [0x00000007bf400000, 0x00000007bf400000, 0x00000007bf500000)
to space 1024K, 0% used [0x00000007bf500000, 0x00000007bf500000, 0x00000007bf600000)
tenured generation total 10240K, used 434K [0x00000007bf600000, 0x00000007c0000000, 0x00000007c0000000)
the space 10240K, 4% used [0x00000007bf600000, 0x00000007bf66c928, 0x00000007bf66ca00, 0x00000007c0000000)
Metaspace used 2726K, capacity 4486K, committed 4864K, reserved 1056768K
class space used 298K, capacity 386K, committed 512K, reserved 1048576K
可以看到第一次调用System.gc()的时候,例子里创建的4M大的byte[]对象并没有被GC掉,而是晋升到old gen了。
此时明明已经离开了局部变量'o'的作用域,而那个4M大的byte[]只被这个局部变量引用,为何HotSpot VM的GC没能回收它?
——因为HotSpot VM的解释器与GC之间的交互有点傻:当被解释执行的方法需要被GC时,计算OopMap的逻辑并没有彻底计算变量的liveness,导致已经无用的局部变量的生命期可能超过其在源码中的作用域范围。
看这个例子对应的字节码:
public static void main(java.lang.String[]);
Code:
stack=1, locals=2, args_size=1
0: ldc #2 // int 4194304
2: newarray byte
4: astore_1
5: aload_1
6: invokevirtual #3 // Method java/lang/Object.hashCode:()I
9: pop
10: invokestatic #4 // Method java/lang/System.gc:()V
13: iconst_0
14: istore_1
15: invokestatic #4 // Method java/lang/System.gc:()V
18: return
LineNumberTable:
line 4: 0
line 5: 5
line 7: 10
line 9: 13
line 11: 15
line 12: 18
LocalVariableTable:
Start Length Slot Name Signature
5 5 1 o Ljava/lang/Object;
15 0 1 i I
0 19 0 args [Ljava/lang/String;
然后通过在一个debug build的HotSpot VM上用 -XX:+TraceNewOopMapGenerationDetailed 参数来跟踪解释器计算OopMap的结果:
java -XX:+TraceNewOopMapGenerationDetailed -XX:+PrintGCDetails -XX:+UseSerialGC -Xmx20m -Xmn10m TestGC相关日志:
static void TestGC.main(jobject)
0 vars = ( r |slot0)( u|Top) ldc
stack =
monitors =
2 vars = ( r |slot0)( u|Top) newarray
stack = ( v |Top)
monitors =
4 vars = ( r |slot0)( u|Top) astore_1
stack = ( r |line2)
monitors =
5 vars = ( r |slot0)( r |line2) aload_1
stack =
monitors =
6 vars = ( r |slot0)( r |line2) invokevirtual()I
stack = ( r |line2)
monitors =
9 vars = ( r |slot0)( r |line2) pop
stack = ( v |Top)
monitors =
10 vars = ( r |slot0)( r |line2) invokestatic()V
stack =
monitors =
13 vars = ( r |slot0)( r |line2) iconst_0
stack =
monitors =
14 vars = ( r |slot0)( r |line2) istore_1
stack = ( v |Top)
monitors =
15 vars = ( r |slot0)( v |Top) invokestatic()V
stack =
monitors =
18 vars = ( r |slot0)( v |Top) return
stack =
monitors =
其中这行日志:
10 vars = ( r |slot0)( r |line2) invokestatic()V
说明这个计算OopMap的逻辑认为在第一次调用System.gc()的地方,局部变量区的slot 0(String[] args)与slot 1(Object o)都还是活的引用类型变量(日志中的标记'r'表示reference)。前面提到了,HotSpot VM并不会在局部变量离开作用域之后对其做显式的清理动作,所以此时slot 1里的值还是指向4M byte[]的引用,导致该数组对象在第一次调用System.gc()时没能被回收。
而第二次调用System.gc()之前,例子通过对复用slot 1的int i变量赋值来达到了“清理引用”的效果,到真正第二次调用System.gc()的地方,计算出来的OopMap是:
15 vars = ( r |slot0)( v |Top) invokestatic()V
这里就只有slot 0还是活引用(标记'r')了,slot 1变为了GC不关心的内容(标记'v'表示void)。
上面例子演示了HotSpot VM在解释执行方法时可能会超越局部变量的静态作用域持有无用的引用,从而有可能隐式延长了对象的存活时间。这恐怕是题主以及许多用HotSpot VM跑Java程序的同学意想不到的行为吧。
===================================================
附录
测试代码:
public class LargeObject {
public void doSomeThing() { }
public static void whatever() { }
public static void main() {
LargeObject lo = new LargeObject();
lo.doSomeThing();
while (true) {
whatever();
}
}
public static void main(String[] args) throws Exception {
main();
}
}
Java版本:Mac OS X上的Oracle JDK8 / OpenJDK8
java -version
java version "1.8.0"
Java(TM) SE Runtime Environment (build 1.8.0-b132)
OpenJDK 64-Bit Server VM (build 25.0-b66-internal-fastdebug, mixed mode)
命令行参数:
java -XX:+PrintOptoAssembly -XX:-TieredCompilation -Xcomp -XX:CompileCommand=compileonly,LargeObject,main -XX:CompileCommand=dontinline,LargeObject,main -XX:+PrintAssembly LargeObject
-XX:+PrintOptoAssembly的日志:
{method}
- this oop: 0x0000000119686458
- method holder: 'LargeObject'
- constants: 0x0000000119686060 constant pool [35] {0x0000000119686060} for 'LargeObject' cache=0x0000000119686580
- access: 0xc1000009 public static
- name: 'main'
- signature: '()V'
- max stack: 3
- max locals: 1
- size of params: 0
- method size: 12
- vtable index: -2
- i2i entry: 0x000000010c7f16e0
- adapters: AHE@0x00007fad0a8600c8: 0x i2c: 0x000000010c8d9820 c2i: 0x000000010c8d9930 c2iUV: 0x000000010c8d9903
- compiled entry 0x000000010c8d9930
- code size: 18
- code start: 0x0000000119686430
- code end (excl): 0x0000000119686442
- method data: 0x00000001196866f0
- checked ex length: 0
- linenumber start: 0x0000000119686442
- localvar length: 1
- localvar start: 0x000000011968644a
#
# void ( )
#
# -- Old rsp -- Framesize: 32 --
#r191 rsp+28: in_preserve
#r190 rsp+24: return address
#r189 rsp+20: in_preserve
#r188 rsp+16: saved fp register
#r187 rsp+12: pad2, stack alignment
#r186 rsp+ 8: pad2, stack alignment
#r185 rsp+ 4: Fixed slot 1
#r184 rsp+ 0: Fixed slot 0
#
abababab N1: # B1 <- B13 Freq: 1
abababab
000 B1: # B7 B2 <- BLOCK HEAD IS JUNK Freq: 1
000 # stack bang
pushq rbp # Save rbp
subq rsp, #16 # Create frame
00c # TLS is in R15
00c movq RAX, [R15 + #120 (8-bit)] # ptr
010 movq R10, RAX # spill
013 addq R10, #16 # ptr
017 cmpq R10, [R15 + #136 (32-bit)] # raw ptr
01e jnb,us B7 P=0.000100 C=-1.000000
01e
020 B2: # B3 <- B1 Freq: 0.9999
020 movq [R15 + #120 (8-bit)], R10 # ptr
024 PREFETCHNTA [R10 + #192 (32-bit)] # Prefetch allocation to non-temporal cache for write
02c movl R11, narrowklass: precise klass LargeObject: 0x00007fad0a8b7a08:Constant:exact * # compressed klass ptr
032 decode_klass_not_null R10,R11
040 movq R10, [R10 + #176 (32-bit)] # ptr
047 movq [RAX], R10 # ptr
04a movl [RAX + #8 (8-bit)], narrowklass: precise klass LargeObject: 0x00007fad0a8b7a08:Constant:exact * # compressed klass ptr
051 movl [RAX + #12 (8-bit)], R12 # int (R12_heapbase==0)
051
055 B3: # B11 B4 <- B8 B2 Freq: 1
055
055 movq RBP, RAX # spill
058 # checkcastPP of RBP
058 movq RSI, RBP # spill
05b call,static LargeObject::<init>
# LargeObject::main @ bci:4 L[0]=_ STK[0]=RBP
# OopMap{rbp=Oop off=96}
060
060 B4: # B10 B5 <- B3 Freq: 0.99998
# Block is sole successor of call
060 movq RSI, RBP # spill
063 call,static LargeObject::doSomeThing
# LargeObject::main @ bci:9 L[0]=_
# OopMap{off=104}
nop # 8 bytes pad for loops and calls
070 B5: # B12 B6 <- B4 B6 Loop: B5-B6 inner Freq: 99996
nop # 3 bytes pad for loops and calls
073 call,static LargeObject::whatever
# LargeObject::main @ bci:12 L[0]=_
# OopMap{off=120}
078
078 B6: # B5 <- B5 Freq: 99994
# Block is sole successor of call
078 jmp,s B5
078
07a B7: # B9 B8 <- B1 Freq: 0.000100017
07a movq RSI, precise klass LargeObject: 0x00007fad0a8b7a08:Constant:exact * # ptr
nop # 3 bytes pad for loops and calls
087 call,static wrapper for: _new_instance_Java
# LargeObject::main @ bci:0 L[0]=_
# OopMap{off=140}
08c
08c B8: # B3 <- B7 Freq: 0.000100015
# Block is sole successor of call
08c jmp,s B3
08c
08e B9: # B13 <- B7 Freq: 1.00017e-09
08e # exception oop is in rax; no code emitted
08e movq RSI, RAX # spill
091 jmp,s B13
091
093 B10: # B13 <- B4 Freq: 9.9998e-06
093 # exception oop is in rax; no code emitted
093 movq RSI, RAX # spill
096 jmp,s B13
096
098 B11: # B13 <- B3 Freq: 1e-05
098 # exception oop is in rax; no code emitted
098 movq RSI, RAX # spill
09b jmp,s B13
09b
09d B12: # B13 <- B5 Freq: 0.99996
09d # exception oop is in rax; no code emitted
09d movq RSI, RAX # spill
09d
0a0 B13: # N1 <- B9 B11 B10 B12 Freq: 0.99998
0a0 addq rsp, 16 # Destroy frame
popq rbp
0a5 jmp rethrow_stub
0a5
-XX:+PrintAssembly的日志:
Decoding compiled method 0x000000010c938950:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000119686458} 'main' '()V' in 'LargeObject'
# [sp+0x20] (sp of caller)
;; N1: # B1 <- B13 Freq: 1
;; B1: # B7 B2 <- BLOCK HEAD IS JUNK Freq: 1
0x000000010c938ac0: mov %eax,-0x16000(%rsp)
0x000000010c938ac7: push %rbp
0x000000010c938ac8: sub $0x10,%rsp ;*synchronization entry
; - LargeObject::main@-1 (line 6)
0x000000010c938acc: mov 0x78(%r15),%rax
0x000000010c938ad0: mov %rax,%r10
0x000000010c938ad3: add $0x10,%r10
0x000000010c938ad7: cmp 0x88(%r15),%r10
0x000000010c938ade: jae 0x000000010c938b3a
;; B2: # B3 <- B1 Freq: 0.9999
0x000000010c938ae0: mov %r10,0x78(%r15)
0x000000010c938ae4: prefetchnta 0xc0(%r10)
0x000000010c938aec: mov $0xf800c006,%r11d ; {metadata('LargeObject')}
0x000000010c938af2: movabs $0x0,%r10
0x000000010c938afc: lea (%r10,%r11,8),%r10
0x000000010c938b00: mov 0xb0(%r10),%r10
0x000000010c938b07: mov %r10,(%rax)
0x000000010c938b0a: movl $0xf800c006,0x8(%rax) ; {metadata('LargeObject')}
0x000000010c938b11: mov %r12d,0xc(%rax)
;; B3: # B11 B4 <- B8 B2 Freq: 1
0x000000010c938b15: mov %rax,%rbp ;*new ; - LargeObject::main@0 (line 6)
0x000000010c938b18: mov %rbp,%rsi
0x000000010c938b1b: callq 0x000000010c8d8de0 ; OopMap{rbp=Oop off=96}
;*invokespecial <init>
; - LargeObject::main@4 (line 6)
; {optimized virtual_call}
;; B4: # B10 B5 <- B3 Freq: 0.99998
0x000000010c938b20: mov %rbp,%rsi
0x000000010c938b23: callq 0x000000010c8d8de0 ; OopMap{off=104}
;*invokevirtual doSomeThing
; - LargeObject::main@9 (line 7)
; {optimized virtual_call}
0x000000010c938b28: nop
0x000000010c938b29: nop
0x000000010c938b2a: nop
0x000000010c938b2b: nop
0x000000010c938b2c: nop
0x000000010c938b2d: nop
0x000000010c938b2e: nop
0x000000010c938b2f: nop
;; B5: # B12 B6 <- B4 B6 Loop: B5-B6 inner Freq: 99996
0x000000010c938b30: nop
0x000000010c938b31: nop
0x000000010c938b32: nop
0x000000010c938b33: callq 0x000000010c8d91e0 ; OopMap{off=120}
;*invokestatic whatever
; - LargeObject::main@12 (line 10)
; {static_call}
;; B6: # B5 <- B5 Freq: 99994
0x000000010c938b38: jmp 0x000000010c938b30
;; B7: # B9 B8 <- B1 Freq: 0.000100017
0x000000010c938b3a: movabs $0x7c0060030,%rsi ; {metadata('LargeObject')}
0x000000010c938b44: nop
0x000000010c938b45: nop
0x000000010c938b46: nop
0x000000010c938b47: callq 0x000000010c8d93e0 ; OopMap{off=140}
;*new ; - LargeObject::main@0 (line 6)
; {runtime_call}
;; B8: # B3 <- B7 Freq: 0.000100015
0x000000010c938b4c: jmp 0x000000010c938b15 ;*new
; - LargeObject::main@0 (line 6)
;; B9: # B13 <- B7 Freq: 1.00017e-09
0x000000010c938b4e: mov %rax,%rsi
0x000000010c938b51: jmp 0x000000010c938b60 ;*invokevirtual doSomeThing
; - LargeObject::main@9 (line 7)
;; B10: # B13 <- B4 Freq: 9.9998e-06
0x000000010c938b53: mov %rax,%rsi
0x000000010c938b56: jmp 0x000000010c938b60 ;*invokespecial <init>
; - LargeObject::main@4 (line 6)
;; B11: # B13 <- B3 Freq: 1e-05
0x000000010c938b58: mov %rax,%rsi
0x000000010c938b5b: jmp 0x000000010c938b60 ;*invokestatic whatever
; - LargeObject::main@12 (line 10)
;; B12: # B13 <- B5 Freq: 0.99996
0x000000010c938b5d: mov %rax,%rsi ;*new ; - LargeObject::main@0 (line 6)
;; B13: # N1 <- B9 B11 B10 B12 Freq: 0.99998
0x000000010c938b60: add $0x10,%rsp
0x000000010c938b64: pop %rbp
0x000000010c938b65: jmpq 0x000000010c907da0 ; {runtime_call}
0x000000010c938b6a: hlt
0x000000010c938b6b: hlt
0x000000010c938b6c: hlt
0x000000010c938b6d: hlt
0x000000010c938b6e: hlt
0x000000010c938b6f: hlt
0x000000010c938b70: hlt
0x000000010c938b71: hlt
0x000000010c938b72: hlt
0x000000010c938b73: hlt
0x000000010c938b74: hlt
0x000000010c938b75: hlt
0x000000010c938b76: hlt
0x000000010c938b77: hlt
0x000000010c938b78: hlt
0x000000010c938b79: hlt
0x000000010c938b7a: hlt
0x000000010c938b7b: hlt
0x000000010c938b7c: hlt
0x000000010c938b7d: hlt
0x000000010c938b7e: hlt
0x000000010c938b7f: hlt
[Stub Code]
0x000000010c938b80: movabs $0x0,%rbx ; {no_reloc}
0x000000010c938b8a: jmpq 0x000000010c938b8a ; {runtime_call}
0x000000010c938b8f: movabs $0x0,%rbx ; {static_stub}
0x000000010c938b99: jmpq 0x000000010c938b99 ; {runtime_call}
0x000000010c938b9e: movabs $0x0,%rbx ; {static_stub}
0x000000010c938ba8: jmpq 0x000000010c938ba8 ; {runtime_call}
[Exception Handler]
0x000000010c938bad: jmpq 0x000000010c9076e0 ; {runtime_call}
[Deopt Handler Code]
0x000000010c938bb2: callq 0x000000010c938bb7
0x000000010c938bb7: subq $0x5,(%rsp)
0x000000010c938bbc: jmpq 0x000000010c8d9da0 ; {runtime_call}
0x000000010c938bc1: hlt
0x000000010c938bc2: hlt
0x000000010c938bc3: hlt
0x000000010c938bc4: hlt
0x000000010c938bc5: hlt
0x000000010c938bc6: hlt
0x000000010c938bc7: hlt