持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第15天,点击查看活动详情
背景
- 对象的创建过程?
- DCL与volatile问题?
- 对象在内存中的存储布局?
- 对象头具体包括什么?
- 对象怎么定位?
- 对象怎么分配?
- Object o = new Object(),在内存中占用多少字节?
过程
- 对象的创建过程 宏观理解
- 当执行new的时候,向jvm申请内存空间,此时会处于半初始化状态,因此这个new也是线程不安全的操作。成员变量此时给的是默认值。
- 当执行invokespecial指令的时候,成员变量会给到程序员赋的值。真正的初始化状态。
- astore_1指令,是让变量o指向初始化状态的对象。
- 对象的创建过程 类加载过程理解。常把验证准备解析理解为连接阶段。
-
加载 a, 通过一个类的全限定名来获取定义此类的二进制字节流。 b, 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。 c, 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区整个类的各种数据的访问入口。
-
验证 a, 确保Class文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机自身的安全。 b, 验证内容:文件格式、元数据验证、字节码验证、符号引用验证。
-
准备 a, 正式为类变量分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区(jdk1.7)中进行分配。 b, 类变量分配内存,在方法区中,赋值是在执行类的构造器 c, 对象内存的分配是在堆中,赋值是在执行对象的构造器 d,因此,static修饰的类变量,会先赋值。给到默认初始值。 e, public static int value = 123, 执行完准备阶段,value值是0
-
解析 虚拟机将常量池内的符号引用替换为直接引用的过程。
-
初始化 这个阶段才真正执行类中定义的Java程序代码,或者是字节码。 执行完初始化阶段: public static int value = 123,value值是123
-
DCL与volatile问题 volatile特性
a, 线程可见性。
b, 禁止指令重排序。
DCL(Double Check Lock)
在写单例的时候,会用到这样的一个写法。
那写DCL的时候,要给成员变量(单例的=变量)添加volatile吗?加,为什么?
理解:假如有A和B两个线程都想new对象。当A线程执行了new指令后,发生了astore_1和invokespecial的指令重排序。这个时候,B线程来了,直接判断对象不为null,拿到引用去调用,发现成员变量值是初始的默认值(半初始化状态),而非程序员设置的值。 因此,写DCL的时候,单例是一定要添加上volatile的。
-
对象在内存中的存储布局
-
对象头具体包括什么 markword中有哪些内容? hashcode, GC相关信息, 锁相关信息。
-
对象怎么定位 句柄方式,还是直接指针方式。主要区别会对GC有影响。
-
对象怎么分配
- 判断能够往栈上分配内存吗?能的话就分配到栈上。如果确定一个对象的作用域不会逃逸出方法之外,那可以将这个对象分配在栈上。
- 大对象,直接分配到Old区。
- 小对象的话,先给到ThreadLocalAllocationBuffer,然后分配到Eden区。
- jvm会给线程分配ThreadLocalAllocaionBuffer这样的私有区域。如果这个区域装满了后,就分配到Eden区。
- Eden S0 S1 相互拷贝,到了一定年龄分配到Old区。
- Object o = new Object(),在内存中占用多少字节 16字节
小结
-
加深面向对象编程的理解。
-
加深jvm的理解。