JVM(九)指针压缩原理-计算对象大小
前言:
Oop-Klass体系回顾,在JVM第一篇中讲过了这部分内容,今天的内容也涉及,回顾一下。
ooPDesc
---------MarkOopDesc:存放锁的信息,分代年龄等等
---------InstanceOopDesc:非数组对象
---------arrayOopDesc:数组对象
-----typeArrayOopDesc:基本数据类型数组,对应的有个存放基本数据类型数组元信息的TypeArrayKlass。
-----objArrayOopDesc:引用数据类型数组,对应有个objArrayKlass存放引用类型的元信息。
我们这里再举个例子,通过HSDB查看在JVM中的对象。
启动HSDB,在jdk1.8/lib下执行:java -cp sa-jdi.jar sun.jvm.hotspot.HSDB
选择main线程,打开stack memroy
第一个就是我们定义的Position对象的地址,第二个是字节数组,第三个是char数组。
我们Inspector查看Position对象。第一部分就是Oop,在对象里面有元数据类型的InstanceKlass。
这里有个重要的点就是:_layout_helper参数。
1:如果是非数组对象,这个大小指的就是类生成的对象的大小。
2:如果是数组对象,是个负值。
3:等于0的话,?
内存布局
我之前写过一篇博客里面提到了这部分内容:https://blog.csdn.net/A7_A8_A9/article/details/105730007?spm=1001.2014.3001.5501
补充:针对上面的内存布局,这里有几点需要说明。
1:对象头中类型指针-class pointer它是指向instanceklass在方法区的地址,如果开启指针压缩的情况下是占4B,如果不开启是占8B。
2:如果对象不是数组,对象头中的数组长度占0B。是数组的话占4字节,因此数组的长度最大为2的32次方-1.
3:实例数据中char类型数据在c++中是用short表示的,所以占两个字节。
4:实例数据中的引用数据类型,如果开启指针压缩占4B,不开启指针压缩占8B。
计算对象大小
指针压缩从jdk1.6之后是默认开启的。可以通过参数控制。
-XX:+UseCompressedOops/-XX:-UseCompressedOops
可以引用:这个依赖来打印对象布局大小。
<dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
1:没有实例数据的对象
测试代码:
public class TestClass { public static void main(String[] args) { TestClass tes1= new TestClass(); System.out.println(ClassLayout.parseInstance(tes1).toPrintable()); } }
结果:一共占16字节。
我们算下,这个16是怎么来的。其实很简单就是:对象头8B+类型指针(压缩)4B+数组长度0B+实例数据0B+4B(对齐填充)=16B
不开启指针压缩就是:对象头8B+类型指针(不开启指针压缩)8B+数组长度0B+实例数据0B=16B 没有了对齐填充也是16B
2:有实例数据
我们给TestClass加两个实例属性。
public class TestClass { int i=2; String name="阿三"; public static void main(String[] args) { TestClass tes1= new TestClass(); System.out.println(ClassLayout.parseInstance(tes1).toPrintable()); } }
开启指针压缩:
Mark Word 8B+类型指针4B(压缩)+数组长度0B+实例数据int 4B+引用数据类型4B(压缩)+4B(对齐填充)=24B
我们看下运行结果:
关闭指针压缩:
对象头8B+类型指针8B(压缩)+数组长度0B+实例数据int 4B+引用数据类型8B(压缩)+4B(对齐填充)=32B
运行结果:
指针压缩
开启指针压缩是为了节省内存,寻址效率有些提高。
指针压缩的原理:
假如分配的内存从0开始且顺序存储,三个对象分别: test1=16B test2=24B test3=32B, 三个地址分别:test1=00000, test2=16(十进制) /10000(二进制),test3=40(十进制) 101000(二进制)。
我们都知道java中的对象都是8字节对齐的,8字节对齐有一个特点就是 1 000,发现了吗 所有对象的指针后三位总是0。这就是指针压缩的点。
压缩原理就是两句话:
1:存储的时候,后三位抹除0.
就变成:test1=00,test2=10
2:使用的时候,后三位补0.
它的指针不再表示对象在内存中的精确位置,而是表示 偏移量 。这意味着 32 位的指针可以引用 40 亿个 对象 , 而不是 40 亿个字节。最终, 也就是说堆内存增长到 32 GB 的物理内存,也可以用 32 位的指针表示。
使用HSDB查看指针压缩现象:
示例代码:
public class TestClass { public static void main(String[] args) { Position position = new Position(1, 2, 3); while(true); } }
关闭指针压缩:
对象地址:
打开指针压缩:
对象大小明显变小了,而且klass属性也不一样了。
这是因为在OopDesc中,指针压缩和不压缩klass存储在不同的地方。
JVM调优基础知识
1:上线前对JVM进行预估调优
2:上线后小规模调优
3:OOM,full GC 频繁调优
主要调什么?
1:方法区
2:虚拟机栈
3:堆区
4:热点代码缓冲区
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现