`
langyu
  • 浏览: 888347 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

[Java拾遗]Java对象大小探究

    博客分类:
  • java
阅读更多

    平时我们不会关心生成的对象到底在JVM中占据多少内存,当发生像OutOfMemory或JVM内存异常增加或减少时才会花精力研究到底发生了什么事情。如果当我们发现有些对象确实很大,但超过我们预期时,我们就该关心下我们所期望创建的每个对象大致会在JVM中占用多少内存呢。这节我会试着以一个更循序诱导的方法来描述,希望可以说的更明白,下面开始:

当遇到OutOfMemory时我们该怎么办?
    一般这个时候,作为我们程序员,心都会焦了,急于想知道到底是哪些对象引起内存不足。我们要做的就是dump heap,然后抓出来分析。这里有一张前些时间我也遇到的OOM问题截图:




    从图上可以抓到“凶手”,剩下的事情就简单多了。

    有个问题:如果现在没有遇到OOM,那我们怎样对某个对象大小有个理性评估呢?

使用Java Instrumentaion来评估每个对象的大小
    Java Instrumentation机制为程序运行提供agent,它的附带功能就是获得已经初始化并准备运行的那个对象大小。这里我采用了javamex.com提供的一个包装util类,方便我们查看对象大小。Jar文件在附件中

    我们的目标类像这样:

public class BytesDemo {

	long a;
	static int kk;
	int ano;

	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 {
	long a;
	static int kk;
	int ano;

	private List<String> cache;

	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. Object memory usage
2. Object memory structure
  • 大小: 45.1 KB
  • 大小: 14.6 KB
  • 大小: 7.1 KB
  • 大小: 7.8 KB
  • 大小: 27.5 KB
0
1
分享到:
评论
2 楼 戴改改 2015-03-30  
对象与对象之间关系的那张图 是不是有问题?
引用类型应该是放在最后的把?
在byte后面。。
padding也应该是在byte后面?

为了节省内存,Sun VM并没有按照属性声明时的顺序来进行内存布局。实际上,属性在内存中按照下面的顺序来组织:

1. 双精度型(doubles)和长整型(longs)

2. 整型(ints)和浮点型(floats)

3. 短整型(shorts)和字符型(chars)

4. 布尔型(booleans)和字节型(bytes)

5. 引用类型(references)
http://www.importnew.com/1305.html
1 楼 xianneng.lin 2012-11-26  
请问楼主一个包含一个int[10][10]属性的Java对象占内存大小怎么算?

相关推荐

Global site tag (gtag.js) - Google Analytics