java创建对象内存分配图解,java对象在内存中存储的结构
00-1010对象内存布局对象占用的内存空间证明对象内存布局以一个问题开始:a Object o,Object o=new Object();创建后会占用多少字节的内存?
要回答这个问题,你需要知道java对象的内存布局。
00-1010一个Java对象在内存中包括三个部分:对象头、实例数据和对齐填充。如下图所示:
对象头
标记字:包含hashcode、GC世代年龄、偏置锁定位、锁定标志位等一系列标记位。这个标记字在对象被不同级别的锁锁住时包含不同的内容和布局,这就涉及到锁升级的知识。我们暂时不讨论它。Klass指针:它是指向描述对象类型的元对象的指针,例如实例数据,如Object.class、User.class
实例数据:描述成员变量的信息。如果成员变量是引用类型,它就是指针。实例数据的大小是所有成员变量的占用空间(基本数据类型大小指针大小)对齐。
填充:在java中,为了更有效地利用内存空间,对象大小将被设置为8字节的整数倍。如果对象头实例数据的大小不是8字节的倍数,则在填充区域中会填充几个字节,因此对象占用的空间是8字节的倍数。对象布局的每个部分占用多少内存空间?
00-1010由于目前64位操作系统已经基本普及,下面只分析64位操作系统下的情况。
指针压缩
在64位系统中,指针占用64位,即8个字节,而在32位系统中,指针只占用4个字节。所以为了减少内存消耗,从JDK1.6开始,JVM默认会支持指针压缩,将指针大小压缩为4个字节。
这里涉及到两个参数-xx3360 usecompresseedoops,-xx3360 usecompressedclassspots。
compressed OOPS:OOPS :官方对象指针,常用对象指针压缩,如Object o=new Object();o是指向新Object()对象的指针。o指针压缩前占用8个字节,指针压缩后占用4个字节:压缩Klass指针,压缩前8个字节,压缩4个字节的对象头。
标志字占用8个字节。
Klass指针:开启(默认)4字节压缩,不开启8字节实例数据.压缩
实例数据:根据实际情况计算:如果一个成员变量是基本数据类型,那么占用的空间就是基本数据类型的大小。Java中八种基本数据类型的大小如下:数据类型占用空间字节byte 1 short 2 int 4 long 8 float 4 double 8 char 2 boolean 1如果一个成员变量是引用类型,那么它就是指针大小(4字节开指针压缩,8字节不开指针压缩)。
对齐
Padding:如果对象头实例数据的大小不是8字节的倍数,那么就填充这个区域,这样对象占用的空间就可以被8字节(最小的情况下)等分。下面将通过实验来证明。
00-1010我们需要参考一个依赖:openjdk提供的jol-core:
dependencygroupidorg . open JDK . jol/groupid artifactidjol-core/artifactidversion 0.9/version/dependency1.查看默认情况下没有成员变量的对象布局
示例代码:
classtestobj { public static void main(string[]args){//Create Object Object o=new Object();//获取
得对象布局内容 String s = ClassLayout.parseInstance(o).toPrintable(); // 打印对象布局 System.out.println(s); }}输出结果:
其中对象头(object header)有三个,前两个是Mark Word一共8个字节,后面一个是Klass Pointer,占4个字节,由于没有成员变量,所以实例数据没有占用空间,而最后4个字节描述信息为:loss due to the next object alignment,意思就是为了与下一个对象对齐而丢失的部分,也就是对齐填充空间
2.证明Klass Pointer在不开启压缩的情况下占用8个字节
我们只需要在jvm参数上加上-XX:-UseCompressedClassPointers即可,在IDEA工具中可以设置启动参数:
还是运行上述代码,运行程序结果:
如上图所示,对象头已经占用16个字节,前8个字节是Mark Word,后8个字节就是未压缩的Klass Pointer。我们还注意到对齐填充也没有了,原因是此时对象占用空间16个字节已经是8bytes的倍数,所以不需要填充,这完全印证了前面的分析
3.证明实例数据的存在以及大小
示例代码:
public class TestObj { public static void main(String[] args) { // 创建对象 User user = new User(1, "zhangsan"); // 获得对象布局内容 String s = ClassLayout.parseInstance(user).toPrintable(); // 打印对象布局 System.out.println(s); }}class User { private int id; private String name; public User(int id, String name) { this.id = id; this.name = name; }}
打印结果:
如上图所示,int类型的id占用4个字节,指向字符串对象的name指针占用4个字节,加上对齐,对象一共占用24 bytes
4.最后验证不开启指针压缩的情况下指针占用8 bytes
只需在jvm参数上加上-XX:-UseCompressedOops:
还是运行上面的代码,打印结果:
很显然,此时name指针已经占用了8个字节
一般来说,UseCompressedClassPointers和UseCompressedOops是默认开启的,我们无需关心也无需修改。但是有个隐藏的细节就是:UseCompressedClassPointers的开启依赖UseCompressedOops的开启,并且开启UseCompressedOops 也默认强制开启UseCompressedClassPointers,关闭UseCompressedOops 默认关闭UseCompressedClassPointers。
至此,关于java对象的内存布局已经有了一个基本的了解,那么文章一开始的问题现在应该能很轻松的回答:16个字节。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持盛行IT。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。