`

JVM中Integer实例占用多少个字节?

    博客分类:
  • java
阅读更多

上篇文章后半部分提到,我们在估算1亿条整数放到内存中,会占用多大的内存的时候,仅仅按照每个Integer 32bit算了,即按照原始类型int来估算的,结果严重超出预料。

仔细想想,对象在jvm中是怎么存的呢?


首先,java对象要包含的基本数据至少要有两部分:

1、类以及超类的实例声明的实例变量;

2、指向类数据的引用,jvm需要通过此引用找到该对象的(可能存在的)方法表、类型信息。其中类型信息包括类型基本信息、常量池、字段信息、方法信息、类变量信息、指向Class的引用、指向ClassLoader的引用等。这些类数据都放在方法区。

另外,还有一些其他信息:

3、多个线程访问同一个对象时候同步用的对象锁lock,这个可以在用到的时候再lazy创建。任何一刻只能有一个线程“拥有“该对象锁。

4、wait set,让多线程为完成统一工作而协调工作用,wiat set与wait和notify方法联合使用。堆中可以为一个wait set的引用,可以在用到的时候lazy 创建。

5、跟踪gc信息,如标示方法的finalize方法是否运行过


但是jvm在堆中怎么存,jvm spec没有做详细的规定。<<深入Java虚拟机>>给出了两种可能的实现方案:

1、把堆分为两部分:一部分为句柄池,另一部分为对象池。每个实例引用都是一个指向句柄池的本地指针,句柄池由指向对象池和类数据的两个指针组成;对象池中只存实例数据。这种表示法的优点是 gc时候,清理完垃圾对象后,还要整理碎片,就需要把非垃圾对象拷贝到一个新的区域,此时由于拷贝,新对象的地址已经改变,但并不需要指向对象的引用值做改变,只用改变句柄池中指向对象池的引用地址即可;缺点是每次访问对象都需要经过两次指针跳转,效率较低。

2、对象指针指向一组数据,改组数据本身包含有实例数据和指向类数据的指针。优缺点跟第一种方案相反。

通过以上分析可得,Integer在内存中有一个指向方法区里边类信息的指针,这个指针占用4bytes;另外Integer中实例变量只有一个int类型的字段,所以为32位,4bytes。在不考虑lock、wait set、gc相关信息占用的时候,如果是第一种方案,有4bytes的指向对象池的指针,一共是3*4=12bytes;如果是第二种实现方案,则是2*4-8bytes的指针。我们怎么能确定jvm用的是第一种方案还是第二种方案呢?lock、wait set、gc相关信息在仅仅创建,累加,销毁的时候是否的确不存在呢?

从VisualVM生成的heap dump文件分析,每个Integer占用了3*4bytes:


从Heap Analyzer上显示信息看就更直接了当了:

但是怎么能确定那额外的根据上边计算,Integer中布确定的4bytes的空间到底是存了什么呢?这个我们还需后续继续深究


继续上篇文章分析:

既然1个Integer占用12bytes,那个1亿条记录也就占用1.2G,加上HashMap中key和value都存的是Integer,一共算上也就2.4G吧,也不应该2kw条记录占了近1g的记录啊。我们是不是漏掉了什么?

的确,我们漏掉了对HashMap本身的分析,从下图看,我们忘记了HashMap$Entry,那一个键值对(即一个Entry)占用多少个字节呢?

要回答这个问题,我们需要分析下Entry的源码了,至少看看它的实例变量吧:


从图中看,有四个实例变量,另外别忘了还有一个指向方法区类信息的指针,一共5*4=20bytes,对么?来看看heap anyalyzer打开的heap dump中的信息:

size为24bytes,跟Integer一样,多出了4bytes,这个是从哪里来的呢?后续文章跟踪分析!另,注意那个total size是指这个Entry本身以及可以到达的所有对象占用的内存空间,此处包括两个Integer,即 key和value占用的空间,为48bytes(Total size :The subtree size of an object is the sum of its size and the sizes of all the objects that it reached from its children. Note that each object is assigned a unique parent and root during )

如此计算下来,1亿整数放入到HashMap中记录每个出现的次数,粗略估算应该占用:48*1亿 = 4.8 * 10亿 = 4.8*2^30 =4.8G,2kw整数也需要占用近1g内存,粗略估算跟实际基本吻合了。

但是从heap dump分析结果看,这100w记录占用空间是56m,而不是48bytes * 100w = 48M,那8m空间是做什么用了呢?HashMap中空的没有使用的空间用了?

至于上边多处关于对象额外4bytes到底存的什么数据的问题,以及100w占用56M而不是48M的问题,后续接着分析!

分享到:
评论

相关推荐

    java面试宝典

    42、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 12 43、说出一些常用的类,包,接口,请各举5 个。 12 44、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以...

    千方百计笔试题大全

    42、一个“.java”源文件中是否可以包含多个类(不是内部类)?有什么限制? 12 43、说出一些常用的类,包,接口,请各举5 个。 12 44、Anonymous Inner Class (匿名内部类) 是否可以extends(继承)其它类?是否可以...

    java面试宝典2012版.pdf

    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 2、Java有没有goto? 3、说说&和&&的区别。 4、在JAVA中如何跳出当前的多重嵌套循环? 5、switch语句能否作用在byte上,能否作用在long上...

    Java面试宝典2010版

    1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制? 2、Java有没有goto? 3、说说&和&&的区别。 4、在JAVA中如何跳出当前的多重嵌套循环? 5、switch语句能否作用在byte上,能否作用在long上...

    最新Java面试宝典pdf版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试笔试资料大全

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    JAVA面试宝典2010

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试宝典-经典

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java面试题大全(2012版)

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java面试宝典2012

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试宝典2012版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    Java面试宝典2012新版

    3、编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串,但要保证汉字不被截取半个,如“我ABC”,4,应该截取“我AB”,输入“我ABC汉DEF”,6,应该输出“我ABC”,而不是“我ABC+汉...

    java 面试题 总结

    与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。...

    超级有影响力霸气的Java面试题大全文档

    当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例...

    java面试题

    84.7. 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+...

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

    3.4.3 第三趟:字节码验证 3.4.4 第四趟:符号引用的验证 3.4.5 二进制兼容 3.5 java虚拟机中内置的安全特性 3.6 安全管理器和java api 3.7 代码签名和认证 3.8 一个代码签名示例 3.9 策略 3.10...

Global site tag (gtag.js) - Google Analytics