浅析java对象内存大小

1. 前言

java程序运行过程中会生成无数的对象,那对象与类关系是怎么样的?可能有部分同学不是很清楚,本文章会介绍统计对象大小的工具,以及借助HSDB分析对象大小

2. 本文收获

阅读本文后能得到如下收获

  • 类方法数量与类的对象大小无关
  • 类的静态字段与类的对象大小无关
  • 父类的成员字段会影响子类的大小

3. 简单对象大小分析

3.1 简单类代码

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
        

3.2 结果输出

64位CPU 且 进程内存小于4G时,默认打印结果如下:

打印对象占堆的大小:32

64位CPU 且 进程内存大于等于4G 或设置 -XX:-UseCompressedOops 参数时,结果如下:

打印对象占堆的大小:40

3.3 结果分析

java内存对象由以下三部分组成: 对象头,实例数据,对齐填充

3.3.1 对象头

对象头包括两部分,
第一部分是:存储对象运行数据,如hashCode,GC分代年龄,销信息…,称为"MarkWord"
第二部分是: 类型指针(klass),即对象指向它的类元数据指针.
其中 64位系统下,
如果使用指针压缩的情况下: MarkWord占8字节 ,类型指针(klass)占4个字节 ;
如果无使用指针压缩的情况下: MarkWord占8字节 ,类型指针(klass)占8个字节 ;

3.3.2 实例数据

类型 占用空间(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字节

3.3.3 对齐填充

按 对象头 和 实例数据 所占的空间大小累计是: 36个字节,而我们上面类打印出来的结果是: 40个字节.
主要原因是java对象一定要以8字节对齐,而36个字节不是8的倍数,所以要补位补到40个字节.

3.4 HSDB查看对象

浅析java对象内存大小_第1张图片
通过HSDB可以查看SimpleObject,从上图可看出:
_mark为MarkWord对象
_metadata为类型指针(klass)
其他几项对应为类的实例数据

4. 含静态字段对象大小分析

4.1 含静态字段类代码

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);
    }
}

4.2 结果输出

在64位,关闭指针压缩情况下,打印如下:

打印对象占堆的大小:40

4.3 结果分析

类含有静态字段与不含静态字段,不影响其内存大小

4.4 HSDB查看对象

浅析java对象内存大小_第2张图片
从HSDB中可以看出SimpleObjectWithStaticField所增加的STATIC_FIELD只在 SimpleObjectWithStaticField类型实例_metadata 的_java_mirror上新增一个STATIC_FIELD实例,对创建的SimpleObjectWithStaticField对象所占的内存大小是不影响的。

5. 有继承关系的对象大小分析

5.1 继承关系类代码

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);
	}
}

5.2 结果输出

在64位,关闭指针压缩情况下,打印如下:
···
打印对象占堆的大小:48
···

5.3 结果分析

原本 SimpleObject是占36个字节空间,而SimpleObjectChildren类的成员字段是long类型,本身是占8个字节,所以加起来44个字节,经字节补齐后便为48个字节.

5.4 HSDB查看对象

浅析java对象内存大小_第3张图片
从SimpleObjectChildren对象图可以看出其父类的private字段也占用其空间

你可能感兴趣的:(java)