参考:
http://langyu.iteye.com/blog/1167581
http://www.javamex.com/tutorials/memory/object_memory_usage.shtml
http://www.codeinstructions.com/2008/12/java-objects-memory-structure.html
http://www.javamex.com/tutorials/memory/array_memory_usage.shtml
下载classmexer.jar
$ javac -cp ./classmexer.jar BytesDemo.java
$ java -javaagent:classmexer.jar BytesDemo
MemoryUtil.memoryUsageOf(demo) 计算demo对象的堆大小
MemoryUtil.deepMemoryUsageOf(demo) 计算demo对象的堆大小,以及他所引用的对象的大小
class Base {
long a;
}
public class BytesDemo {
Base b = new Base();
public static void main(String[] args) {
BytesDemo demo = new BytesDemo();
System.out.println("Object size : " + MemoryUtil.memoryUsageOf(demo));
System.out.println("Object size : " + MemoryUtil.deepDemoryUsageOf(demo));
}
}
输出为:16,32
从上面的一张图上看到了两个名词: shallow size 和 retained size 。Shallow表示本对象自身的大小是多少。本对象可能会直接或间接引用其它很多对象,如果被引用的对象仅仅被本对象所引用,那么当本对象无用被GC时,那么本对象所引用的对象也会被GC。Retained就表示如果当本对象被GC时能相关地减少的内存量。
shallow size就是MemoryUtil.memoryUsageOf()的值,
我认为大小关系:shallow size<=retained size<=MemoryUtil.deepDemoryUsageOf()
classmexer对于2种情况计算并不准确,例如Threalocal等不再堆中存放的对象,对象是否包含synchronized锁,是否在gc。
We'll generally be talking about the memory taken up on the heap by a given object under "normal circumstances". A couple of minor complications that we'll gloss over are that:
对象占的内存包含一下:
In general, the heap memory used by a Java object in Hotspot consists of:
每个对象的物理存储可以分为两部分:Header及该对象的所有对象属性值(不包括 static属性) :
(1) Hotspot VM限定每个对象header是2个word,word是JVM内部的存储最小单位,当前Hotspot定义的word大小是4字节,所以header共是8个字节。Header中应当包含着本对象的hashCode,对象锁及与GC相关的生存周期信息等。
Instances of an object on the Java heap don't just take up memory for their actual fields. Inevitably, they also require some "housekeeping" information, such as recording an object's class, ID and status flags such as whether the object is currently reachable, currently synchronization-locked etc.
Other JVMs probably have a similar object overhead.
(2) 对象属性分为两部分:基本类型属性与对象引用。 Java事先定义了所有基本类型的占用位数 ,如下表:
基本类型对象属性依上表占用着堆内存,而每个对其它对象的引用是规定占用一个word,也就是4个字节。 像上面第二个目标类中新增加一个对象引用cache,那么这个引用属性就只占用4个字节。
正常的对象引用也有两种:普通对象与数组。数组也是正常对象,只不过,它除了header外还有4个字节表示当前数组的长度是多少,那么我们也可以认为数组对象的header长度就是12个字节了。
A single-dimension array is a single object. As expected, the array has the usual object header . However, this object head is 12 bytes to accommodate a four-byte array length . Then comes the actual array data which, as you might expect, consists of the number of elements multiplied by the number of bytes required for one element, depending on its type.
在这里要特别强调的是如果某个普通对象就包含一个byte属性,那么它的对象大小应该是9个字节。而JVM为了malloc与gc方便,指定分配的每个对象都需要是8字节的整数倍 ,那么对象大小就不再是9个字节,而应该是16个字节。
In Hotspot, every object occupies a number of bytes that is a multiple of 8 . If the number of bytes required by an object for its header and fields is not a multiple 8, then you round up to the next multiple of 8 .
This means, for example, that:
多维数组的内存占用情况:
For example, let's consider a 10x10 int array. Firstly, the "outer" array has its 12-byte object header followed by space for the 10 elements. Those elements are object references to the 10 arrays making up the rows. That comes to 12+4*10=52 bytes, which must then be rounded up to the next multiple of 8, giving 56. Then, each of the 10 rows has its own 12-byte object header, 4*10=40 bytes for the actual row of int s, and again, 4 bytes of padding to bring the total for that row to a multiple of 8. So in total, that gives 11*56=616 bytes. That's a bit bigger than if you'd just counted on 10*10*4=400 bytes for the hundred "raw" int s themselves.