java对象大小及浅堆、深堆、保留内存的概念

2018-01-05 10:45:37来源:oschina作者:幽冥领域人点击

分享

以下所有数据均为64位环境下。


java对象大小由以下几部分组成:对象头 + 实例数据+ 对齐填充。


对象头

对象头 = 标记部分(mark word) + 原始对象引用


标记部分记录了该对应的运行时数据,如hashCode、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。这部分大小在32位机器上为4byte,64位机器上为8byte。


原始对象引用就是对象的指针,通过这个指针找到对象的实例,该数据可以压缩。这部分大小在32位机器上为4byte,在64位机器上为8byte,如果开启了压缩(UseCompressedOops),大小为4byte。jdk8默认开启压缩。


由上述概念可知,一个对象头的大小 = 标记部分(8byte) + 原始对象引用(未压缩:8byte/压缩:4byte)=未压缩:16byte/压缩:12byte。


实例数据

原始数据大小



类型
大小(单位:byte)


byte
1


boolean
1


char
2


short
2


int
4


float
4


double
8


long
8



对象引用大小为32位 4byte,64位8byte(开启压缩后为4byte)。


对齐填充

对齐填充说的是任何对象都用8byte来对齐,所以对象的大小为8的整数倍。


所以一个没有任何参数的控对象,对象大小为16byte。


浅堆

浅堆大小为对象本身的大小,浅堆大小 =对象头 + 实例数据 + 对齐填充


深堆

深堆大小是对象所涉及的所有对象的大小之和。


保留内存

保留内存是指只能通过该对象访问的对象浅堆之和。


举例:假设5个对象:A,B,C,D,E。A引用B和C,D引用C和E


A的深堆大小 = A + B + C


A的保留内存大小 = A + B,因为D引用了C。


保留内存为对象回收后,肯定释放的内存,上述例子中,回收A,肯定会回收B,但是不会回收C,因为D引用了C。


实例代码:


package com.test
public class Test {
A a = new A();
B b = new B();
public static void main(String[] args) throws Exception {
Test t = new Test();
System.in.read();
}
}
class A {
private String a = "a";
private byte[] b = new byte[1024 * 2];
}
class B {
private double d = 2D;
}

使用 jmap -dump获得程序的内存快照,在mat(MyEclipse Memory Analyzer tools)中打开,找到com.test.Test类,查看浅堆,深堆(Retained Heap虽然字面上是保留内存,但是实际是指的深堆)大小。


开启压缩,图如下:



可以看到com.test.Test的浅堆大小为24byte,深堆大小为2184byte。


根据代码分析com.test.Test对象浅堆大小 = 对象头(12byte)+ A引用(4byte) + B引用(4byte)+ 对齐填充(4byte) = 24byte


com.test.A对象浅堆大小 = 对象头(12byte) + 字符串a引用(4byte)+ 数组引用(4byte)+ 对齐填充(4byte)=24byte


com.test.B对象浅堆大小 =对象头(12byte)+ 基本类型double变量(8byte)+ 对齐填充(4byte)= 24byte


byte数组b浅堆大小 = 对象头(12byte)+ 2048 * 基本类型byte的大小 = 12 + 2048 = 2064byte


String类中包含两个参数int类型的hash 和 char数组,所以String类型的a也占24byte。

微信扫一扫

第七城市微信公众平台