标签:标记 父类 创建 cto 清除 内存布局 static blog 如何
对象的创建
对象创建的几种方式
一、 用new来创建
二、 克隆
三、 Class对象和Constructor中的newInstance()方法
四、 反序列化
对象创建的过程(不包括数组对象和Class对象的创建)
一、 类加载,如果该对象对应的类还没有被加载到内存中则会就行类的加载,可能会涉及到父类的加载(具体看类的加载过程)
二、 分配内存,当类加载完成后对象所需的内存就可以确定,就可以为其分配内存,在内存分配时有两种方法:
1、 指针碰撞法:将指针作为已经分配内存和未分配内存的分界线,当给对象分配内存时指针做相应的移动,这种方法要求内存空间是规整的
2、 空闲列表法:找到一块足够大的空间划分非给对象,并且标记该内存块,这种方法不要求内存规整但是会造成碎片空间,如何在两种方法中进行选择是由堆是否规则决定的,而堆是否规整则又收到垃圾回收器对垃圾回收算法的选择(通常为两种:标记-整理和标记-清除),标记-整理算法会维持堆的规整,而标记-清除则不会
在内存分配时由于并发操作可能存在一些错误,通常解决并发问题的方法有下面两种:
1、 CAS失败重试
2、 把内存分配的动作按照线程划分在不同的空间中进行(TLAB:本地线程分配缓冲)
三、 初始化内存为零值,由于这部操作实例变量在未进行初始化就可以使用
四、 设置对象的相关信息,设置对象头中的相关参数,例如对象的哈希码、分代年龄,具体可以看对象的内存布局。
五、 执行init()方法,init方法中是我们在定义类时再类中赋初值的成员的赋值,不包括被static或者final修饰的成员,他们在类加载阶段就被赋值。
对象的内存布局:
对象的内存主要分为三部分:对象头、实例数据、对齐填充
一、对象头:对象头又分为运行时数据、类型指针。哈希码、分代年龄、锁状态标志、线程持有的锁等都属于运行时数据,类型指针用于确定对象属于哪个类的实例。
二、实例数据:存储对象中的信息,例如类中的字段
三、对齐填充:只起到填充作用
对象的访问定位
在使用对象时,我们是通过在栈中创建一个对象的引用,通过对象的引用再找到实际的对象实例,而对象的引用找到实际的对象实例有两种方式:一种是句柄,另一种是直接指针
使用句柄:使用句柄时在堆中有分配了一个区域---句柄池,句柄池中包含实例数据指针和对象类型数据指针,再通过这两个指针指向实例数据和对象类型数据,这样的话当堆中发生垃圾回收后导致实例数据的地址改变时只会引起句柄池中实例数据指针的改变,而不会导致栈中对象引用的改变
使用直接指针:使用直接指针后就不需要使用句柄池,对象的引用直接指向对象,这样就减少了实例数据指针指向实例数据这一个过程,所以使用直接指针比使用句柄更加高效,但是当对象的地址改变时对象的引用的值也会改变。
说明:对象的引用变量是在虚拟机栈中分配的,实例数据属于对象内存的一部分,在堆中分配,对象的类型数据其实就是类的相关数据,是在方法区中分配的,在对象的内存布局的时候我们就知道对象的对象头中有一个类型指针,该指针就是指向方法区中的类的,所以使用句柄和直接指针的两种方式这一个指向过程都是不能省略的。
图片引用:https://blog.csdn.net/hbtj_1216/article/details/77599990
标签:标记 父类 创建 cto 清除 内存布局 static blog 如何
原文地址:https://www.cnblogs.com/zhaolei1996/p/10952646.html