对象的创建和空间分配解析

JVM对象专题

对象的创建和空间分配解析

一、对象的创建过程

1.对象的创建顺序

  1. class loading 创建

  2. class linking 链接

  3. class initializing 类初始化

  4. 申请对象内存

  5. 成员变量赋默认值

  6. 调用构造方法

    a.成员变量顺序赋初始值

    b.调用构造方法

        初始化时对应代码的执行顺序:

        静态成员变量 -> 静态代码块 -> 普通成员变量 -> 普通代码块 -> 构造方法

        对应验证代码:

public class Test4 {
    static A a = new A();
    static {
        System.out.println("静态代码块");
    }
    B b = new B();
    {
        System.out.println("普通代码块");
    }


    public Test4(){
        System.out.println("构造方法");
    }
    public static void main(String[] args) {
       new Test4();
    }
}
class A{
    public A(){
        System.out.println("静态成员变量");
    }
}
class B{
    public B(){
        System.out.println("普通成员变量");
    }
}

◆ ◆ ◆  ◆ ◆

如果加载对象有父类怎么执行呢?

        父类静态成员变量 -> 父类静态代码块 -> 子类静态成员变量 -> 子类静态代码块 -> 父类普通成员变量 -> 父类普通代码块 -> 父类构造方法 -> 子类普通成员变量 -> 子类普通代码块 -> 子类构造方法

        验证代码:

public class Test4 extends Father {
    static A a = new A();


    static {
        System.out.println("静态代码块");
    }


    B b = new B();


    {
        System.out.println("普通代码块");
    }


    public Test4() {
        System.out.println("构造方法");
    }


    public static void main(String[] args) {
        new Test4();
    }
}


class A {
    public A() {
        System.out.println("静态成员变量");
    }
}


class B {
    public B() {
        System.out.println("普通成员变量");
    }
}


class Father {
    C c = new C();
    static D d = new D();
    static {
        System.out.println("父类静态代码块");
    }
    {
        System.out.println("父类普通代码块");
    }
    public Father() {
        System.out.println("父类构造方法");
    }


}
class C {
    public C() {
        System.out.println("父类普通成员变量");
    }
}
class D {
    public D() {
        System.out.println("父类静态成员变量");
    }
}
输出:
父类静态成员变量
父类静态代码块
静态成员变量
静态代码块
父类普通成员变量
父类普通代码块
父类构造方法
普通成员变量
普通代码块
构造方法

        顺带提一句:如果是static final类型成员变量,初始化分为两种情况

a.基本类型

        在编译阶段,已经给static final类型赋值,并且保存在常量池中。

如图:常量池中存在 init i = 9

对象的创建和空间分配解析_第1张图片

b.引用类型

        将在初始化截断 initializing 阶段创建对象赋值

        验证代码:

class Test5 {


    final static Test5 a = new Test5();
    static int i = 1;
    final static int j = 9;
    final static InnerA innerAFinal = new InnerA();


    static {
        System.out.println("初始化阶段 " + "i="+i+"  j="+j);
    }


    public Test5(){
        System.out.println("init i = "+i);
        System.out.println("init j = "+j);
        System.out.println("init innerAFinal = "+innerAFinal.a);
    }


    public static void main(String[] args) {
        Test5 b = new Test5();
    }
}
class InnerA{
    int a = 1;
    {
        System.out.println("创建成员变量"+a);
    }
}
结果:
init i = 0 //说明非final变量还未初始化,只是有默认值
init j = 9 //说明非final变量初始化前已经赋值
Exception in thread "main" java.lang.ExceptionInInitializerError
Caused by: java.lang.NullPointerException
  at com.mashibing.jvm.dc.Test5.(Test5.java:17)
  at com.mashibing.jvm.dc.Test5.(Test5.java:5)
  //说明引用类型在初始化阶段才开始创建对象

        关于类加载过程的详细解答,下次会专门再讲。

◆ ◆ ◆  ◆ ◆

2.对象在内存中的布局

1.普通对象

  1. 对象头:markword 8字节

  2. ClassPointer指针:指向类 T.class -XX:+UseCompressedClassPointers 为4字节 不开启为8字节

  3. 实例数据

  1. 引用类型:-XX:+UseCompressedOops 为4字节 不开启为8字节 Oops Ordinary Object Pointers

  2. 基本数据类型:见下表

    对象类型 字节
    boolean 1
    byte 1
    short 2
    char 2
    int 4
    float 4
    long 8
    double 8
  1. Padding对齐,要求是8的倍数。

        所以一般来说,一个对象最小是16字节(没有成员变量)

举例:

public class Test {
    int id;
}

        开启压缩普通对象指针时,对象大小:(8+4)+4=16字节,是8的倍数,补齐0字节最终大小为16字节关闭压缩普通对象指针时,对象大小:(8+8)+4=20字节,不是8的倍数,补齐4字节最终大小为24字节

2.数组对象

对象头:markword 8

  1. ClassPointer指针同上

  2. 数组长度:4字节

  3. 数组数据

  4. 对齐 8的倍数

◆ ◆ ◆  ◆ ◆

关注并后台回复 “面试” 或者  “视频”,

即可免费获取最新2019BAT

大厂面试题和大数据微服务视频

您的分享和支持是我更新的动力

·END·

后端开发技术

追求技术的深度

微信号:后端开发技术

觉得不错“在看”支持一下~ 

↓↓↓

你可能感兴趣的:(对象的创建和空间分配解析)