一、对象的创建过程
- 定位到方法区常量池的类,检查类是否已经被加载、验证、准备、解析。
- 检查类是否已经进行过初始化。
- 在内存中分配空间
- 设置对象的默认初始值(0,false,null)
- 执行<clint>()方法(类变量的赋值和静态代码块)
- 执行构造
- 在线程栈中设置对象引用
二、 对象在内存中的布局:
对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(InstanceData)和对齐填充(Padding)。
每一个堆中的对象在HosPost虚拟机中都有一个对象头(ObjectHeader),对象头里存储两部分信息:一部分是运行时对象自身的哈希码值(HashCode)、GC分代年龄(Generational GC Age)、锁标志位(占用2个bit的位置)等信息,另一部分是指向方法去中的类型信息的指针。如果是数组对象的话,还会有额外一部分用来存储数组长度。
实例数据是程序代码中定义的各种类型的字段内容。
对齐填充并不是必然存在的,仅仅在对象的大小不是8字节的整数倍时起占位符的作用。
三、对象的访问方式:1,句柄;2,直接指针
句柄:java堆中划出一块空间单独存放句柄池,java栈中的reference指向句柄池中的某个句柄,句柄包含两部分信息:堆中实例对象地址和方法区中对象类型信息。
直接指针:java栈中的reference指向java堆中的实例对象,每个实例对象上有一部分信息是用来指向该对象在方法区中的类型信息。
句柄的优势:当对象发生改变时,reference的值不用变,只需要改变句柄的实例指针,reference自身不需要改变。
直接指针的优势:节省了一次指针定位的开销,速度更快。
HosPot虚拟机采用第二种方式。
四、对象完成使命后,等待GC进行垃圾回收。销毁对象即清理对象所占用的内存空间,会调用对象的finalize()方法。
注意:
1,在类进行初始化的时候,静态代码块与静态变量的赋值的顺序是按照程序员编写的顺序执行的; 2,在创建对象的<clint>(),是按照程序员编写的代码块和成员变量的赋值的顺序执行的。下面的例子,创建后的对象的属性a=22,类属性TEST_STATIC=11.
public static class TestStaticInit{ static{ TEST_STATIC = 1; } public static int TEST_STATIC = 11; { a = 2; } public int a = 22; }
参考资料:
- 《深入理解Java虚拟机》