我使用的是64位Ubuntu 14,Intel Core i5。
我编写了以下简单程序来理解对象分配在Java中的工作原理:
public class App {
public static void main(String[] args) {
for(int i = 0; i < Integer.MAX_VALUE; i++)
testObjectCreationCompiled();
}
public static void testObjectCreationCompiled() {
Object obj = new Object();
if (obj.hashCode() == System.nanoTime()) {
System.out.print("");
}
}
}
我按如下方式运行此程序:
java -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*App.testObjectCreationCompiled -server -jar target/test-1.0.0.jar
在查看编译后的代码后,我想知道对象分配是如何发生的(编译代码片段):
0x00007f60651165e4: mov %r12d,0xc(%rsi) ;*new ; - com.test.App::testObjectCreationCompiled@0 (line 13)
0x00007f60651165e8: mov (%rsi),%r10
0x00007f60651165eb: mov %r10,%r11
0x00007f60651165ee: and $0x7,%r11
0x00007f60651165f2: cmp $0x1,%r11
0x00007f60651165f6: jne 0x7f606511662a
0x00007f60651165f8: shr $0x8,%r10
0x00007f60651165fc: mov %r10d,%ebp
0x00007f60651165ff: and $0x7fffffff,%ebp
0x00007f6065116605: test %ebp,%ebp
0x00007f6065116607: je 0x7f606511662a ;*invokevirtual hashCode
; - com.test.App::testObjectCreationCompiled@9 (line 14)
0x00007f6065116609: movabs $0x7f6079d5d440,%r10
0x00007f6065116613: callq %r10 ;*invokestatic nanoTime
; - com.test.App::testObjectCreationCompiled@13 (line 14)
0x00007f6065116616: movsxd %ebp,%r10 ;*i2l ; - com.test.App::testObjectCreationCompiled@12 (line 14)
0x00007f6065116619: cmp %rax,%r10
0x00007f606511661c: je 0x7f6065116649 ;*ifne
; - com.test.App::testObjectCreationCompiled@17 (line 14)
0x00007f606511661e: add $0x10,%rsp
0x00007f6065116622: pop %rbp
0x00007f6065116623: test %eax,0x15f3d9d7(%rip) ; {poll_return}
0x00007f6065116629: retq
0x00007f606511662a: nop
0x00007f606511662b: callq 0x7f6065046020 ; OopMap{off=144}
;*invokevirtual hashCode
; - com.test.App::testObjectCreationCompiled@9 (line 14)
; {optimized virtual_call}
0x00007f6065116630: mov %eax,%ebp
0x00007f6065116632: jmp 0x7f6065116609
0x00007f6065116634: movabs $0x100000f28,%rsi ; {metadata('java/lang/Object')}
0x00007f606511663e: nop
0x00007f606511663f: callq 0x7f6065100fa0 ; OopMap{off=164}
;*new ; - com.test.App::testObjectCreationCompiled@0 (line 13)
; {runtime_call}
以下new Object()
标记为mov %r12d,0xc(%rsi)
。
看起来此时内存已经分配了r12d
的地址。
问题是为什么我们mov
地址到[rsi+0xc]
内存位置。
但据我所知,在linux中分配一些内存我们必须执行sys_brk
系统调用。我们在这里做的只是简单的mov
指令,我在这里看不到任何syscall
。为什么简单mov
表示new Object()
?
对象分配如何在JVM中工作?
答案 0 :(得分:2)
给定的片段不完整。实际的分配代码应该在这个片段的正上方。
mov %r12d,0xc(%rsi)
是分配序列的最后一条指令 - 它只是将新对象的最后一个填充字清零。
我已经在this,this和this答案中描述了HotSpot中对象分配的工作原理。由于JVM不依赖于系统分配器,因此您不会在那里看到任何系统调用。它在预分配区域中使用自己的内存管理 - Java Heap。
以下是C2编译代码中分配序列的外观。评论是我的。
mov 0x60(%r15),%rdx ; obj = currentThread.tlab_top
mov %rdx,%r10
add $0x10,%r10 ; r10 = obj + sizeof(java/lang/Object)
cmp 0x70(%r15),%r10 ; if (r10 >= currentThread.tlab_end)
jae 0x00000000030ad2f4 ; goto slow_case
mov %r10,0x60(%r15) ; currentThread.tlab_top = r10
prefetchnta 0xc0(%r10) ; prefetch memory next to tlab_top into CPU caches
; to make subsequent allocations faster
mov $0x200001e5,%r10d ; r10 = VMKlass of java/lang/Object
shl $0x3,%r10
mov 0xa8(%r10),%r10 ; r10 = Header prototype for java/lang/Object
mov %r10,(%rdx) ; obj[0] = r10 (header prototype)
movl $0x200001e5,0x8(%rdx) ; obj[8] = VMKlass of java/lang/Object
mov %r12d,0xc(%rdx) ; obj[12] = 0 (padding to 8-byte boundary)