一个Java对象成员占用内存会占多大内存

博客分类:
&&& 平时我们不会关心生成的对象到底在JVM中占据多少内存,当发生像OutOfMemory或JVM内存异常增加或减少时才会花精力研究到底发生了什么事情。如果当我们发现有些对象确实很大,但超过我们预期时,我们就该关心下我们所期望创建的每个对象大致会在JVM中占用多少内存呢。这节我会试着以一个更循序诱导的方法来描述,希望可以说的更明白,下面开始:
当遇到OutOfMemory时我们该怎么办?
&&& 一般这个时候,作为我们程序员,心都会焦了,急于想知道到底是哪些对象引起内存不足。我们要做的就是dump heap,然后抓出来分析。这里有一张前些时间我也遇到的OOM问题截图:
&&& 从图上可以抓到“凶手”,剩下的事情就简单多了。
&&& 有个问题:如果现在没有遇到OOM,那我们怎样对某个对象大小有个理性评估呢?
使用Java Instrumentaion来评估每个对象的大小
&&& Java Instrumentation机制为程序运行提供agent,它的附带功能就是获得已经初始化并准备运行的那个对象大小。这里我采用了提供的一个包装util类,方便我们查看对象大小。Jar文件在附件中
&&& 我们的目标类像这样:
public class BytesDemo {
public static void main(String[] args) {
BytesDemo demo = new BytesDemo();
System.out.println("Object size : " + MemoryUtil.memoryUsageOf(demo));
&&& 这个程序在运行时,它会显示多少呢?
&&& (这里可以看到,我的运行是使用了classmexer.jar,并且运行在class文件所在的编译目录,如果你不能获得这样的输出结果,那么请检查classmexer.jar是否放对位置和是否在class文件所在目录上执行java)
&&& 如果我们的目标类变成这样
public class BytesDemo {
private List&String&
public static void main(String[] args) {
BytesDemo demo = new BytesDemo();
demo.cache = new ArrayList&String&();
demo.cache.add("firstKey");
System.out.println("Object size : " + MemoryUtil.deepMemoryUsageOf(demo));
&&& 请注意,这里新增加了了一个属性,而且对MemoryUtil调用的方法也不一样,它的结果是:
对象与对象之间的关系
&&& 对象都存储于JVM堆中,对象与对象之间通过引用链接-直接指向目标对象的物理位置。这里有一张图,是我自己对堆内对象存储情况的理解,事实可能有些不一致,仅仅是让我们有直观印象
&&& 每个对象的物理存储可以分为两部分:Header及该对象的所有对象属性值(不包括static属性)。Hotspot VM限定每个对象header是2个word,word是JVM内部的存储最小单位,当前Hotspot定义的word大小是4字节,所以header共是8个字节。Header中应当包含着本对象的hashCode,对象锁及与GC相关的生存周期信息等。对象属性分为两部分:基本类型属性与对象引用。Java事先定义了所有基本类型的占用位数,如下表:
&&& 基本类型对象属性依上表占用着堆内存,而每个对其它对象的引用是规定占用一个word,也就是4个字节。像上面第二个目标类中新增加一个对象引用cache,那么这个引用属性就只占用4个字节。
&&& 正常的对象引用也有两种:普通对象与数组。数组也是正常对象,只不过,它除了header外还有4个字节表示当前数组的长度是多少,那么我们也可以认为数组对象的header长度就是12个字节了。
&&& 在这里要特别强调的是如果某个普通对象就包含一个byte属性,那么它的对象大小应该是9个字节。而JVM为了malloc与gc方便,指定分配的每个对象都需要是8字节的整数倍,那么对象大小就不再是9个字节,而应该是16个字节。
&&& 在了解上面的这些对象计算规则后,也请大家计算上面两个目标类的对象大小是否符合预期。
&&& 从最上面的一张图上看到了两个名词: shallow size 和 retained size。Shallow表示本对象自身的大小是多少。本对象可能会直接或间接引用其它很多对象,如果被引用的对象仅仅被本对象所引用,那么当本对象无用被GC时,那么本对象所引用的对象也会被GC。Retained就表示如果当本对象被GC时能相关地减少的内存量。这里有个说的十分详细。
类的继承关系对对象大小的影响
&&& 类如果有继承关系,那么按Java的定义,如果某个子类想要被初始化,就得先初始化自己的父类,这样子类对象其实也包含着所有其父类的非static属性。这种关系同样作用于像内部类这种结构里。
参考资料:
1.
下载次数: 267
浏览: 720689 次
来自: 杭州
楼主你人呢,搬家了吗。还想看你的文章
写的不错,比书上还具体,受益匪浅
&div class=&quote_title ...
&div class=&quote_title ...
&div class=&quote_title ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'Java的自动内存管理机制()省却了很多编码工作,大大地提高了Java的生产力,而且JVM的性能也越来越好,特别是的出现,改善了垃圾回收中stop the world的状况。
也许很多人都没有考虑过这个问题,new一个Object对象到底占用多少内存呢( Object obj = new Object() )?
这里很明确的是obj是一个指向对象的引用(),引用的长度决定了Java的寻址能力,32位的JDK是4字节,64位的JDK是8字节()。
因为obj对象没有任何数据(field),会在堆上为它分配空间吗?如果分配空间,里面存储了什么内容?
以面向对象的思维来分析,对象封装了数据和行为,是一个统一的整体,虽然obj对象没有数据,但是有行为(Object类定义了12个方法)。当我们执行完new操作后,obj的值为堆内存的地址,既然obj都指向一块内存了,说明是会在堆上为其分配空间的。
那么分配的空间有多大,存储了什么内容呢?在和里面没有找到相关的描述,这很可能是属于JVM实现自由控制的范畴了。我们可以利用JDK自带的工具jvisualvm.exe来查看分配的空间有多大。为了方便在jvisualvm中查看对象占多少内存,这里使用一个私有的静态内部类EmptyObject来替代Object,因为类定义为空,所以可以等同对待EmptyObject和Object。
&*&构造一个无任何字段的空对象占多少内存
&*&@author&杨尚川
public&class&EmptyTest&{
&&&&public&static&void&main(String[]&args)&throws&InterruptedException{
&&&&&&&&//加到集合中,使垃圾无法回收
&&&&&&&&List&EmptyObject&&emptys&=&new&ArrayList&&();
&&&&&&&&for(int&i=0;i&100;i++){
&&&&&&&&&&&&emptys.add(new&EmptyObject());
&&&&&&&&//打开jvisualvm,查看EmptyObject的大小为16字节
&&&&&&&&Thread.sleep(60*1000);
&&&&private&static&class&EmptyObject{}
我们在这里面通过new不同的对象数(for循环次数),来分析内存占用,new 1个对象是16字节,new 2个对象是32字节,new 100个对象是1600字节,通过很多次的尝试,我们从jvisualvm里面可以看到 字节数=对象数*16 ,我们有理由相信对象数跟字节数的线性关系。从这里可以看出,jvisualvm显示的内存占用跟引用的4字节或8字节是没有关系的,也就是说,jvisualvm显示的是堆内存占用,这也很好理解,毕竟所有引用的字节占用是固定的。8字节是引用,16字节是堆内存,总共是8+16=24字节,所以new一个Object对象占用8+16=24字节(64位JDK)。
如果JDK是32位,按如上分析方法可知new一个Object对象占用4+8=12字节(32位JDK),如下图所示:
64位JDK:32位JDK:
那么分配的16字节(8字节)的堆内存中存储了什么内容呢?当我们Object obj = new Object();的时候,在栈内存中有一个引用obj,他可能是32位也可能是64位,obj实质只是一个内存地址,那么当我们调用obj.xxx()的时候,JVM怎么知道obj是哪个类的实例呢?所以,可以大胆地推测,obj对象的16字节(8字节)的堆内存中记录了对象属于哪个类的信息,问题是这16字节(8字节)的结构是什么样的?不清楚!
不过我们仍然可以大胆地猜测一下,通过上面的64位和32位的堆内存大小对比分析发现,堆内存分配的大小是引用的两倍,上面我们已经猜测堆内存中会记录对象是哪个类的实例,如何记录呢?因为类对象是放置在方法区的,类对象本身也是一个对象,因此可以通过一个引用指向它,所以堆内存有可能就是放置了两个引用,指向两个对象。分析到这里,事情就比较明朗了,堆内存中可能就放置了两个内存地址,一个指向EmptyObject.class(在实验代码中用EmptyObject来代替Object),一个指向什么呢?不清楚!
在 2nd by Bill Venners的5.3.5中有这么一段描述:
The Java virtual machine specification is silent on how objects should be represented on the heap. Object representation--an integral aspect of the overall design of the heap and garbage collector--is a decision of implementation designers.&
好了,事情最终清楚了,JVM规范并没有规定Java对象在堆中是如何表示的,对象的表示是堆和垃圾收集器的整体设计的一个组成部分,这是由JVM实现的设计师来决定的。&因此,如果我们真的想搞清楚对象是如何表示的,那么需要查询HotSpot VM或是其他JVM实现的相关资料。
在淘宝工程师的《》PPT的第112页介绍了&HotSpot中的Java对象布局&,这真是现在关心的内容,通过PPT的介绍说明前面的猜测是对的,如下图所示:
我们研究new一个Object对象占多少内存可能没什么实际意义,因为我们在编程的时候就可以确定对象树,基本可以确定对象大小,除了变长字段,当然,变长字段我们一般也会有长度限制。所以我们真正关心的是所有数据最终的大小,也就是数据库的大小。
面向对象的分析、设计和编程都把&封装&奉为圭臬,&分层&更是架构设计中至关重要的设计准则。因为只有这样,才能实现基本的解耦、让协作分工成为可能,满足工业要求的最大化生产力的最终目标。
那么这种没有什么实际意义的问题为什么要研究呢?我觉得只能用三个字来形容:好奇心。好奇心是驱使我们研究技术的强大推力,当我们工作了很多年,尤其是在不重视技术的公司,我们对技术还有激情吗?保持一颗敏感好奇的心,也许技术之路可以走的更长更远。
这篇文章的重点是展示一种分析问题的思路,要大胆猜测,小心求证,追本溯源,引经据典。求证方式:查找标准规范、查找经典权威书籍、自己做实验、查找源代码等。
参考资料:
阅读(...) 评论() &博客分类:
查看已用内存
System.out.println("--- Memory Usage:"); /*打印一行字符串---Memory Usage*/
Runtime rt=Runtime.getRuntime( ); //获得系统的Runtime对象rt
//打印总内存大小 //打印空闲内存大小 //打印已用内存大小 单位(字节)
System.out.println("Total Memory= " + rt.totalMemory()+
" Free Memory = "+rt.freeMemory()+" Used Memory="+(rt.totalMemory()-rt.freeMemory()));
public abstract class SizeOf {
private final Runtime s_runtime = Runtime.getRuntime();
* 子类负责覆盖该方法以提供被测试类的实例
* @return 被测试类的实例
protected abstract Object newInstance();
* 计算实例的大小(字节数)
* @return 实例所占内存的字节数
* @throws Exception
public int size() throws Exception {
// 垃圾回收
// 提供尽可能多(10万)的实例以使计算结果更精确
final int count = 100000;
Object[] objects = new Object[count];
// 实例化前堆已使用大小
long heap1 = usedMemory();
// 多实例化一个对象
for (int i = -1; i & ++i) {
Object object =
// 实例化对象
object = newInstance();
if (i &= 0) {
objects[i] =
// 释放第一个对象
// 垃圾收集
// 实例化之前堆已使用大小
heap1 = usedMemory();
// 实例化之后堆已使用大小
long heap2 = usedMemory();
final int size = Math.round(((float) (heap2 - heap1)) / count);
// 释放内存
for (int i = 0; i & ++i) {
objects[i] =
private void runGC() throws Exception {
// 执行多次以使内存收集更有效
for (int r = 0; r & 4; ++r) {
private void _runGC() throws Exception {
long usedMem1 = usedMemory();
long usedMem2 = Long.MAX_VALUE;
for (int i = 0; (usedMem1 & usedMem2) && (i & 500); ++i) {
s_runtime.runFinalization();
s_runtime.gc();
Thread.currentThread().yield();
usedMem2 = usedMem1;
usedMem1 = usedMemory();
* 堆中已使用内存
* @return 堆中已使用内存
private long usedMemory() {
return s_runtime.totalMemory() - s_runtime.freeMemory();
public class SizeOfObject extends SizeOf {
protected Object newInstance() {
return new Object();
public static void main(String[] args) throws Exception {
SizeOf sizeOf = new SizeOfObject();
System.out.println("所占内存:" + sizeOf.size() + "字节");
输出为:所占内存:8字节 利用序列化(Serializable)计算对象的大小 下面代码可以计算session的大小: 将session中的所有对象输出到文件中,文件的大小就是对象的大小.
FileOutputStream f = new FileOutputStream("c:/sessionFiles");
ObjectOutputStream s = new ObjectOutputStream(f);
s.writeObject("session:");
HttpSession session = request.getSession(false);
Enumeration names = session.getAttributeNames();
while(names.hasMoreElements()){
s.writeObject(session.getAttribute((String) names.nextElement()));
s.flush();
s.close();
f.close();
} catch (Exception e) {
e.printStackTrace();
也可以看看这个 java对象占内存大小 http://.iteye.com/blog/547779
浏览: 1227171 次
来自: 北京
代码报异常啊
mysql的注释符号“-- ”,“#”,“/**/”要加上吗? ...
Thanks for sharing!
多谢楼主分享!
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'java 中new一个对象占多少内存? - ITeye问答
java 中new一个对象占多少内存?要怎么计算呢?
采纳的答案
Object o=new Object():
&&& 在java中空对象占八个字节,对象的引用占四个字节。所以上面那条语句所占的空间是4byte+8byte=12byte.java中的内存是以8的倍数来分配的,所以分配的内存是16byte.
举个例子:
Class O{
&
&
& S
}
其所占内存的大小是空对象(8)+int(4)+byte(1)+String引用(4)=17byte,因要是8的整数倍,所以其占大小为24byte.
当然,如果类里有其他对象的话,也要把其他对象的空间算进去。
其实这个在javaeye的电子期刊上有。
用io流& object流转换成文件流& 写到磁盘 看大小
&&&&&&&& 或者用内存流 byteArray& 得到了一个字节数组 看数组的长度就知道多少字节了
已解决问题
未解决问题博主最新文章
博主热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 java 对象占用内存 的文章

 

随机推荐