以前写过一些,请参考传送门:
答复: 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”。这些位置是:
回头看题主的例子,假设代码里的调用点都没有被内联的话:
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分别是什么:
再多废话几句:大家都知道System.gc()可以让Java代码主动触发GC,但从HotSpot VM的角度看,讨论safepoint与OopMap时,System.gc()的调用点其实是个“被动safepoint”。
专门指出这个是为了避免读者误会这里说的主动/被动。强调一下,这里说的:
既然只有这些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