java程序运行过程中会生成无数的对象,那对象与类关系是怎么样的?可能有部分同学不是很清楚,本文章会介绍统计对象大小的工具,以及借助HSDB分析对象大小
阅读本文后能得到如下收获
import org.apache.lucene.util.RamUsageEstimator;
public class SimpleObject {
private int intVal;
private double doubleVal;
private Object obj;
public void method1(){
}
public void method2(){
}
public int getIntVal() {
return intVal;
}
public void setIntVal(int intVal) {
this.intVal = intVal;
}
public static void main(String[] args) {
long size = RamUsageEstimator.shallowSizeOf(new SimpleObject());
System.out.println("打印对象占堆的大小:"+size);
}
}
maven依赖
org.apache.lucene
lucene-core
4.0.0
64位CPU 且 进程内存小于4G时,默认打印结果如下:
打印对象占堆的大小:32
64位CPU 且 进程内存大于等于4G 或设置 -XX:-UseCompressedOops 参数时,结果如下:
打印对象占堆的大小:40
java内存对象由以下三部分组成: 对象头,实例数据,对齐填充
对象头包括两部分,
第一部分是:存储对象运行数据,如hashCode,GC分代年龄,销信息…,称为"MarkWord"
第二部分是: 类型指针(klass),即对象指向它的类元数据指针.
其中 64位系统下,
如果使用指针压缩的情况下: MarkWord占8字节 ,类型指针(klass)占4个字节 ;
如果无使用指针压缩的情况下: MarkWord占8字节 ,类型指针(klass)占8个字节 ;
类型 | 占用空间(byte) |
---|---|
boolean | 1 |
byte | 1 |
char | 2 |
short | 2 |
int | 4 |
float | 4 |
long | 8 |
double | 8 |
引用类型在开启压缩策略下占用4字节空间 ,否则占8个字节
以关闭指针压缩配置为例子分析Simple类占的内存空间:
对象元素 | 大小(byte) |
---|---|
MarkWord | 8 |
类型指针(klass) | 8 |
intVal:int | 4 |
doubleVal:double | 8 |
obj:Object | 8 |
共计:36字节
按 对象头 和 实例数据 所占的空间大小累计是: 36个字节,而我们上面类打印出来的结果是: 40个字节.
主要原因是java对象一定要以8字节对齐,而36个字节不是8的倍数,所以要补位补到40个字节.
通过HSDB可以查看SimpleObject,从上图可看出:
_mark为MarkWord对象
_metadata为类型指针(klass)
其他几项对应为类的实例数据
import org.apache.lucene.util.RamUsageEstimator;
public class SimpleObjectWithStaticField {
public final static String STATIC_FIELD = "static";
private int intVal;
private double doubleVal;
private Object obj;
public void method1(){
}
public void method2(){
}
public int getIntVal() {
return intVal;
}
public void setIntVal(int intVal) {
this.intVal = intVal;
}
public static void main(String[] args) {
long size = RamUsageEstimator.shallowSizeOf(new SimpleObjectWithStaticField());
System.out.println("打印对象占堆的大小:"+size);
}
}
在64位,关闭指针压缩情况下,打印如下:
打印对象占堆的大小:40
类含有静态字段与不含静态字段,不影响其内存大小
从HSDB中可以看出SimpleObjectWithStaticField所增加的STATIC_FIELD只在 SimpleObjectWithStaticField类型实例_metadata 的_java_mirror上新增一个STATIC_FIELD实例,对创建的SimpleObjectWithStaticField对象所占的内存大小是不影响的。
import org.apache.lucene.util.RamUsageEstimator;
public class SimpleObjectChildren extends SimpleObject{
private long longVal;
public static void main(String[] args) {
long size = RamUsageEstimator.shallowSizeOf(new SimpleObjectChildren());
System.out.println("打印对象占堆的大小:"+size);
}
}
在64位,关闭指针压缩情况下,打印如下:
···
打印对象占堆的大小:48
···
原本 SimpleObject是占36个字节空间,而SimpleObjectChildren类的成员字段是long类型,本身是占8个字节,所以加起来44个字节,经字节补齐后便为48个字节.