Java中的对象
对象的创建过程
- 首先完成类的初始化操作,加载类
- 为对象申请内存空间
- 成员变量 赋默认值
- 调用构造方法<init>
- 成员变量顺序赋初始值
- 执行构造方法语句
- 父类构造函数
- 子类
对象在内存中的布局
以64位机器为目标
普通对象
-
对象头 markword 8字节
-
类指针,ClassPointer 8字节 开启压缩(-XX:UseCompressedClassPointers 默认开启)是4字节 用于找到对象所属的类
-
实例数据
-
若有引用类型:-XX:UseCompressedOops为4字节 不开启为8字节
Oops ordinary object pointers 普通对象指针
-
若无引用类型 按基本数据类型的大小计算
-
-
padding 填充 8的倍数
数组对象
- 对象头 markword,同上
- 类指针 ,同上
- 数组长度 4字节
- 实例大小
- padding对齐 填充为8的倍数
为什么需要padding?
主要因为HotspotVM自动内存管理系统要求对象起始地址必须是8个字节的整数倍,也就是说对象的大小必须是8个字节的整数倍,而对象头部分整好是8个字节的整数倍。因此,对象实例数据部分如果没有对齐是就需要通过padding来进行填充。
对象头包括什么
-
无锁情况下:主要包括hashcode、分代年龄,是否偏向 0,锁标志位
hashcode 如果没有重写过,也就是根据内存计算的hashcode identityHashCode() 会存储在markword中,重写过则不会存储.如果计算过identityHashCode则无法进入偏向锁状态。在重量级锁情况下在ObjectMonitor下会存储,也就是说,重量级锁存储了identityHashCode
-
偏向锁情况下:偏向线程的ID,Epoch,是否偏向 为1,锁标志位
-
轻量级锁:指向栈中lock record所在位置,锁标志位
-
重量级锁:指向互斥量的指针,锁标志位
对象定位
-
句柄池 也就是间接方式 一个存储着对象地址与类地址的句柄
![image-20201215221050440]()
-
直接指针 通过指针直接获取对象,然后通过对象中的class pointer找到class
![image-20201215221038075]()
Object对象占多少字节
根据对象里的内容计算
-
对象头 markword 8字节
-
类指针,ClassPointer 8字节 开启压缩(-XX:UseCompressedClassPointers)是4字节 用于找到对象所属的类
-
实例数据
-
引用类型:-XX:UseCompressedOops为4字节 不开启为8字节
Oops 面向对象
-
-
padding 填充 8的倍数
包括对象头,类指针,无实例数据。不开启压缩的情况下8+8=16。
开启压缩后类指针变为4字节这样就会有12字节,但是呢,由于padding的存在会将其填充为8的倍数,也就是16字节。因此,无论是否开启类指针压缩,Object大小都是16字节
空的int数组对象大小
对象头8,类指针8,数组大小4 =20 填充为8的倍数24.若开启压缩后类指针变为4字节 所以一共是8+4+4=16字节 正好为8的倍数padding填充0
为什么GC年龄默认为15(最大为15)
因为分代年龄只有4bit 范围为0~15

对象分配
堆上分配
绝大多数对象分配的地方
栈上分配
- 经过逃逸分析后不会发生逃逸
所谓的逃逸分析,就是对象不会逃出方法,如果方法中生成的对象被外界引用或者通过返回值返回,这样就是发生逃逸了
- 标量替换
所谓标量替换 就是可以用对象里边的属性可以代表这个对象 所以隐含的意思就是对象成员变量比较小也就是对象小
- 对象小
线程本地分配
在Eden区中为每个线程分配1%的空间,方便线程快速分配对象,
如果设置了虚拟机参数 -XX:UseTLAB,在线程初始化时,同时也会申请一块指定大小的内存,只给当前线程使用,这样每个线程都单独拥有一个空间,如果需要分配内存,就在自己的空间上分配,这样就不存在竞争的情况,可以大大提升分配效率。
from:https://www.jianshu.com/p/8be816cbb5ed
本文来自博客园,作者:小鸡小鸡快点跑,转载请注明原文链接:https://www.cnblogs.com/clion/p/14141379.html



浙公网安备 33010602011771号