JVM常用概念之新对象实例化
前言
对于大部分的语言,新对象的实例化基本分为三个阶段:分配、系统初始化、用户初始化。
分配
在分配阶段主要需要确定进程空间中是堆区、还是栈区来获取实例数据。
系统初始化
系统初始化指的是特定编程语言的初始化,在C编程语言中,new分配的对象不需要进行初始化,而在Java编程语言中,所有的对象都需要进行系统初始化,具体包含将JVM分配到的内存空间初始化为零值,设置对象头(包含对象的哈希码、GC分代年龄、对象的类元数据信息等)等。
用户初始化
主要指运行与当前对象有关联的任何实例初始化程序和构造函数。
示例
源码
public class Case{
public Object newInstance(){
return new Object();
}
}
编译
public class Case {
<ClassVersion=55>
<SourceFile=Case.java>
public Case() { // <init> //()V
L0 {
aload 0 // reference to self
invokespecial java/lang/Object.<init>()V
return
}
}
public java.lang.Object newInstance() { //()Ljava/lang/Object;
L0 {
new java/lang/Object
dup
invokespecial java/lang/Object.<init>()V
areturn
}
}
}
实验
源码
package org.sample;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
public class UserInit {
@Benchmark
public Object init() {
return new Init(42);
}
@Benchmark
public Object initLeaky() {
return new InitLeaky(42);
}
static class Init {
private int x;
public Init(int x) {
this.x = x;
}
}
static class InitLeaky {
private int x;
public InitLeaky(int x) {
doSomething();
this.x = x;
}
@CompilerControl(CompilerControl.Mode.DONT_INLINE)
void doSomething() {
// intentionally left blank
}
}
}
编译
public class org/sample/UserInit {
<ClassVersion=50>
<SourceFile=UserInit.java>
public UserInit() { // <init> //()V
<localVar:index=0 , name=this , desc=Lorg/sample/UserInit;, sig=null, start=L0, end=L1>
L0 {
aload 0 // reference to self
invokespecial java/lang/Object.<init>()V
return
}
L1 {
}
}
public java.lang.Object init() { //()Ljava/lang/Object;
<localVar:index=0 , name=this , desc=Lorg/sample/UserInit;, sig=null, start=L0, end=L1>
<visAnno:desc = Lorg/openjdk/jmh/annotations/Benchmark; , values = []>
L0 {
new org/sample/UserInit$Init
dup
bipush 42
invokespecial org/sample/UserInit$Init.<init>(I)V
areturn
}
L1 {
}
}
public java.lang.Object initLeaky() { //()Ljava/lang/Object;
<localVar:index=0 , name=this , desc=Lorg/sample/UserInit;, sig=null, start=L0, end=L1>
<visAnno:desc = Lorg/openjdk/jmh/annotations/Benchmark; , values = []>
L0 {
new org/sample/UserInit$InitLeaky
dup
bipush 42
invokespecial org/sample/UserInit$InitLeaky.<init>(I)V
areturn
}
L1 {
}
}
// The following inner classes couldn't be decompiled: org/sample/UserInit$InitLeaky org/sample/UserInit$Init
}
Init()部分汇编
; ------- allocation ----------
0x00007efdc466d4cc: mov 0x60(%r15),%rax ; TLAB allocation below
0x00007efdc466d4d0: mov %rax,%r10
0x00007efdc466d4d3: add $0x10,%r10
0x00007efdc466d4d7: cmp 0x70(%r15),%r10
0x00007efdc466d4db: jae 0x00007efdc466d50a
0x00007efdc466d4dd: mov %r10,0x60(%r15)
0x00007efdc466d4e1: prefetchnta 0xc0(%r10)
; ------- /allocation ---------
; ------- system init ---------
0x00007efdc466d4e9: movq $0x1,(%rax) ; put mark word header
0x00007efdc466d4f0: movl $0xf8021bc4,0x8(%rax) ; put class word header
; ...... system/user init .....
0x00007efdc466d4f7: movl $0x2a,0xc(%rax) ; x = 42.
; -------- /user init ---------
在Init()方法中,没有严格的区分系统初始化和用户初始化的顺序。
InitLeaky()部分汇编
; ------- allocation ----------
0x00007fc69571bf4c: mov 0x60(%r15),%rax
0x00007fc69571bf50: mov %rax,%r10
0x00007fc69571bf53: add $0x10,%r10
0x00007fc69571bf57: cmp 0x70(%r15),%r10
0x00007fc69571bf5b: jae 0x00007fc69571bf9e
0x00007fc69571bf5d: mov %r10,0x60(%r15)
0x00007fc69571bf61: prefetchnta 0xc0(%r10)
; ------- /allocation ---------
; ------- system init ---------
0x00007fc69571bf69: movq $0x1,(%rax) ; put mark word header
0x00007fc69571bf70: movl $0xf8021bc4,0x8(%rax) ; put class word header
0x00007fc69571bf77: mov %r12d,0xc(%rax) ; x = 0 (%r12 happens to hold 0)
; ------- /system init --------
; -------- user init ----------
0x00007fc69571bf7b: mov %rax,%rbp
0x00007fc69571bf7e: mov %rbp,%rsi
0x00007fc69571bf81: xchg %ax,%ax
0x00007fc69571bf83: callq 0x00007fc68e269be0 ; call doSomething()
0x00007fc69571bf88: movl $0x2a,0xc(%rbp) ; x = 42
; ------ /user init ------
在InitLeaky()方法中,由于优化器无法确定是否需要x的值,它们必须假设最坏的情况,首先执行系统初始化,然后再完成用户初始化。