了解 Java 中的对象分配

Understanding object allocation in Java

我正在使用 Ubuntu 14 64 位,英特尔酷睿 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     [=13=]x7,%r11
  0x00007f60651165f2: cmp     [=13=]x1,%r11
  0x00007f60651165f6: jne     0x7f606511662a
  0x00007f60651165f8: shr     [=13=]x8,%r10
  0x00007f60651165fc: mov     %r10d,%ebp
  0x00007f60651165ff: and     [=13=]x7fffffff,%ebp
  0x00007f6065116605: test    %ebp,%ebp
  0x00007f6065116607: je      0x7f606511662a    ;*invokevirtual hashCode
                                                ; - com.test.App::testObjectCreationCompiled@9 (line 14)

  0x00007f6065116609: movabs  [=13=]x7f6079d5d440,%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     [=13=]x10,%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  [=13=]x100000f28,%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 中是如何工作的?

给定的片段不完整。实际分配代码应该在这个片段的正上方。

mov %r12d,0xc(%rsi) 是分配序列的最后一条指令 - 它只是将新对象的最后一个填充字清零。

我已经在 this, this and this 回答中描述了 HotSpot 中对象分配的工作原理。你不会在那里看到任何系统调用,因为 JVM 不依赖于系统分配器。它在预分配区域使用自己的内存管理 - Java Heap.

C2 编译代码中的分配顺序通常如下所示。评论是我的。

    mov    0x60(%r15),%rdx        ; obj = currentThread.tlab_top
    mov    %rdx,%r10
    add    [=10=]x10,%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    [=10=]x200001e5,%r10d      ; r10 = VMKlass of java/lang/Object
    shl    [=10=]x3,%r10
    mov    0xa8(%r10),%r10        ; r10 = Header prototype for java/lang/Object
    mov    %r10,(%rdx)            ; obj[0] = r10 (header prototype)
    movl   [=10=]x200001e5,0x8(%rdx)  ; obj[8] = VMKlass of java/lang/Object
    mov    %r12d,0xc(%rdx)        ; obj[12] = 0 (padding to 8-byte boundary)