(1)对象的创建方法:new的过程
第一:进行类加载检查。当遇到一个new指令,首先检查能否在方法区的常量池中能否定位到这个类的符号引用,并且检查类有没有进行加载、解析和初始化;
第二:分配空间。有两种常见的分配方式,一是指针碰撞,二是空闲列表,分别针对连续分配内存和不连续的,有空隙的,取决于虚拟机是否会压缩整理。内存分配的大小是在类加载完成之后就已经确定的,但是分配的时候修改指针的指向位置应该是线程安全的(栈上的Reference),第一种方式就保证原子性;第二种是给每个线程分配自己的一小块内存,成为本地线程分配缓冲(TLAB),每个线程在自己的TLAB是哪个分配。
第三:初始化。将分配的内存初始化为0值。
第四:基本设置。进行基本的设置,确定这个对象是哪个类的实例,对象的HASH码,对象的年龄等等。
(2)对象的内存布局
对象在内存中的存储的布局可以分为3块区域:
第一:对象头(Header)
对象头包含两个部分的信息,第一部分是对象自身的运行时数据,如哈希码、GC分代年龄、持有的锁等等;第二部分是类型指针,指向它的类元数据的指针,通过这个虚拟机来确定这个对象是哪个类的实例。
第二:实例数据(Instance Data)
对象真正存储的数据,就是程序代码中定义的字段内容。
第三:对齐填充(Padding)
用于使对象的开头必须是8字节的整数倍,无特殊意义。
(3)对象的访问定位
对于这句代码:
1. Object objectRef = new Object();
Object objectRef 这部分将会反映到Java栈的本地变量中,作为一个reference类型数据出现。而“new Object()”这部分将会反映到Java堆中,形成一块存储Object类型所有实例数据值的结构化内存,根据具体类型以及虚拟机实现的对象内存布局的不同,这块内存的长度是不固定。另外,在java堆中还必须包括能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些数据类型存储在方法区中。
有两种基本的定位方式:
第一:句柄访问(间接)
在Java堆中划分一块内存作为句柄池(即一个句柄列表),reference中存储的就是对象在句柄池中的地址,得到了句柄池的地址就可以知道对象的实例数据和类型数据的位置。
第二:直接指针访问(直接)
reference中存储的直接就是对象的实例数据的地址,而实例数据中自己有一个指针存储对象类型数据的地址(方法区中),不需要reference来存储。
---------------------
原文:https://blog.csdn.net/qq_39037047/article/details/80532908