理解Java中的对象分配

理解Java中的对象分配,java,performance,object,dynamic-memory-allocation,jit,Java,Performance,Object,Dynamic Memory Allocation,Jit,我使用的是Ubuntu 14 64位,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 v

我使用的是Ubuntu 14 64位,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("");
        }
    }
}
看过编译后的代码后,我想知道对象分配是如何发生的(编译后的代码片段):

以下是
newobject()
标记为just
mov%r12d,0xc(%rsi)

看起来此时已分配内存,地址位于
r12d

问题是,我们为什么要将
mov
地址设置为
[rsi+0xc]
内存位置

但据我所知,要在linux中分配一些内存,我们必须执行
sys\u brk
syscall。我们在这里所做的只是简单的
mov
指令,我从来没有在这里看到任何
syscall
。为什么简单的
mov
意味着
新对象()


对象分配在JVM中是如何工作的?

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

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

我已经在中描述了对象分配在热点中的工作原理,并给出了答案。因为JVM不依赖于系统分配器,所以在这里您不会看到任何系统调用。它在预分配区域Java堆中使用自己的内存管理

下面是分配序列在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)

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

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

我已经在中描述了对象分配在热点中的工作原理,并给出了答案。因为JVM不依赖于系统分配器,所以在这里您不会看到任何系统调用。它在预分配区域Java堆中使用自己的内存管理

下面是分配序列在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)

JVM通常使用
malloc
(可能使用您的sys\u brk)分配大内存块,然后将大内存块的较小部分划分为单个对象。可能在执行上述代码之前很久就分配了第一块内存,您没有注意到系统调用。@markspace某种编译器优化?但有可能以某种方式追踪它吗?否则听起来像是魔法。你能建议一些工具吗?是的,这是一个优化,因为对
malloc
的许多小调用比单个调用花费的时间要多得多。在Java中无法跟踪单个对象的创建,当您使用JVM时,您必须放弃那种逐字节的思维。要跟踪总体内存,请查看VisualVM、jmap和jstat等工具。简单的程序不太适合理解复杂的事情。通过C++来理解java的“眼镜”也是一个有害的角度。你过于依赖于编译的代码,而不是研究Java工作背后的理论。我一直认为Hotspot是一个很好的文档和开放的平台。当然,如果你在寻找简单快捷的答案,你可能会失望。然而,如果你在寻找简单而快速的答案,我想知道你为什么选择这样一个具体而复杂的领域来研究?您是否经历过这些:JVM通常使用
malloc
(可能使用您的sys\u brk)分配大内存块,然后将大内存块的较小部分分配给各个对象。可能在执行上述代码之前很久就分配了第一块内存,您没有注意到系统调用。@markspace某种编译器优化?但有可能以某种方式追踪它吗?否则听起来像是魔法。你能建议一些工具吗?是的,这是一个优化,因为对
malloc
的许多小调用比单个调用花费的时间要多得多。在Java中无法跟踪单个对象的创建,当您使用JVM时,您必须放弃那种逐字节的思维。要跟踪总体内存,请查看VisualVM、jmap和jstat等工具。简单的程序不太适合理解复杂的事情。通过C++来理解java的“眼镜”也是一个有害的角度。你过于依赖于编译的代码,而不是研究Java工作背后的理论。我一直认为Hotspot是一个很好的文档和开放的平台。当然,如果你在寻找简单快捷的答案,你可能会失望。然而,如果你在寻找简单而快速的答案,我想知道你为什么选择这样一个具体而复杂的领域来研究?你有没有经历过这些:
    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)