Java中对象占用内存大小计算

IMG_1933.jpg

可以直接进入正题对象的组成这一节

byte与bit

bit: 位,比特。 信息的最小单位,二进制数中的一个位数(二进制位),其值为“0”或“1”;

byte: 字节。计算机文件大小的基本计算单位;

  • 原码:正数本身,负数,正数对应的二进制最高位为1(负);
  • 反码:正数本身,负数,符号位不变,其余各位取反;
  • 补码:正数本身,负数:反码+1
  • 0的反码、补码都为零

注意: 计算机处理是以补码形式,我们最终看到的是原码形式

例如:

System.out.println((byte)233); // -23
System.out.println((byte)-233); // 23
System.out.println("~b2: " + ~10); // -11

列表

Java基本类型

注意:

  • 对于数组32,默认为int型,32B为byte类型,32S为short类型,32L为long型
  • 基本数据类型自动转换(低转高,高转低会丢失精度)
    • byte -> short
    • char -> int -> long
    • float -> double
    • int -> float
    • long -> double

后面会出一篇关于低转高,高转低的计算博文,敬请期待!

Java中对象占用内存大小

对象的组成

可用如下一张图来概括

对象组成图

具体大小

名称(单位byte) 32位 64位 开启指针压缩后(指针对64位有效且默认开启)
对象头(Header) 8 16 12
数组对象头 12 24 16
引用(reference) 4 8 4
  • 开启指针压缩指令-XX:+UseCompressedOops,关闭指令-XX:-UseCompressedOops,只在64位才有效且默认开启;
  • 数组对象头比普通对象多了个数组长度;
对象头(Header)

“用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,这部分数据的长度在32位和64位的虚拟机(未开启压缩指针)中分别为32bit和64bit,官方称它为"Mark Word”

实例数据(Instance Data)

“对象真正存储的有效信息,也是在程序代码中所定义的各种类型的字段内容。无论是从父类继承下来的,还是在子类中定义的,都需要记录起来”

对其补充(Padding)

“第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说,就是对象的大小必须是8字节的整数倍。而对象头部分正好是8字节的倍数(1倍或者2倍),因此,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。”

HotSpot的对齐方式为8字节对齐:

(Header + Instance Data + Paddding) % 8 = 0 并且 0 <= padding < 8

实战

ShallowRetained区别

再看具体的例子之前,需要了解下两个名词,下面使用Java性能监控工具Jprofile会用到

  • Shallow Size 对象自身占用的内存大小,不包括它引用的对象
    • 针对非数组类型的对象,它的大小就是对象与它所有的成员变量大小的总和。当然这里面还会包括一些java语言特性的数据存储单元。
    • 针对数组类型的对象,它的大小是数组元素对象的大小总和。
  • Retained Size Retained Size=当前对象大小+当前对象可直接或间接引用到的对象的大小总和。(间接引用的含义:A->B->C, C就是间接引用)

注意:以下实验均在此环境下进行:

java version "1.8.0_171"
Java(TM) SE Runtime Environment (build 1.8.0_171-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)

实例一:

public class A {
    private int i;

    public static void main(String[] args) throws InterruptedException {
        A a = new A();
        Thread.sleep(1000 * 1000);
        System.out.println(a);
    }
}

分析: 64位下默认开启指针压缩,对象头位12byte, i4byte,此时12 + 4 = 16 可以整除8,所以padding=0,最终

12(header) + 4(instance data)+0(padding)=16byte jprofile结果如下:

image

如果我们关掉指针压缩,

image

16(header) + 4(instance data)+4(padding)=24byte 16+4=20,不能整除8,需要再加上4Jprofile如下

image

实例二

public class B {
   private int i = 5;
   private Integer ii = 128;

   public static void main(String[] args) throws InterruptedException {
      B b = new B();
      Thread.sleep(1000 * 1000);
      System.out.println(b);
   }
}

比较特殊的地方时这里复制了,因为包装类型有自己的缓存,可以看这里*

开启指针压缩,计算内存大小

Shallow Size: 12(B Header) + 4 (i instance) + 4 (ii reference) + 4(padding) = 24bytes

Retained Size: 12(B Header) + 4 (i instance) + 4 (ii reference) + (12(ii header) + 4(instance)+ 0(padding)) + 4(padding) = 40bytes

Jprofile如下

image

实例三

public class C {
   private int i;
   private char[] cc;

   public C() {
      i = 5;
      cc = new char[]{'a', 'b', 'c'};
   }

   public static void main(String[] args) throws InterruptedException {
      C c = new C();
      Thread.sleep(1000 * 1000);
      System.out.println(c);
   }
}

多了数组,注意数组自己本身的padding

Shallow Size: 12(C Header) + 4 (i instance) + 4 (cc reference) + 4(padding) = 24bytes

Retained Size: 12(C Header) + 4 (i instance) + 4 (cc reference) + (16(cc header) + 2(instance) * 3+ 2(padding)) + 4(padding) = 48bytes

Jprofile如下

5.png

实例四

public class D {
    private Map<String, String> map;

    public D() {
        map = new HashMap<>();
        map.put("A", "A");
    }

    public static void main(String[] args) throws InterruptedException {
        D d = new D();
        Thread.sleep(1000 * 1000);
        System.out.println(d);
    }
}

Shallow Size: 12(D header) + 4(map reference) + 0(padding) = 16bytes

基本上Shallow Size很容易就算出来

但是Retained Size就异常的复杂,首先我们要去了解HashMap的结构

transient Node<K,V>[] table;
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
transient int modCount;
int threshold;
final float loadFactor

// 来自AbstractMap
transient Set<K>        keySet;
transient Collection<V> values;

Node的结构如下:

final int hash;
final K key;
V value;
Node<K,V> next;

注:此处的K,V是String类型

再看String的结构

    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0

以下是自己的计算过程,结果是对了,但不知道具体过程是否正确,仅供参考!!!

  • D对象12(D header) + 4(map reference)=16

  • HashMap:12(header) + 4(table ref) + 4(entrySet ref) + 4(size) + 4(modCount) + 4(thresload) + 4(float) + 4(keySet ref) + 4(values ref) + 4(padding)=48

  • Node[]: 16(header) + 4(hash) + 4(key ref) + 4(val ref) + 4(next ref) + 0(padding)=32

  • K与V:(12(hader) + 4(hash) + 4(value ref) + (16(value header) + 1 * 2(char)) + 2(padding) = 40) * 2=80

最终:16 + 48 + 32 + 80 = 176!!!

image

总结

最后总结一下,计算一个对象的大小需要注意一下几步:

  • Shallow 与Retained的区别;

  • 对象本身的大小;

  • 对象引用的对象也要注意对其补充,保证其也是被8整除的;

  • 复杂对象需要深入分析

  • 机器位数

能快速估算出每一个对象占用空间的大小可以在编程的时候正确选择数据结构,有兴趣得可以看一下这一节

“5.2.6 不恰当数据结构导致内存占用过大”

摘录来自: 周志明. “深入理解Java虚拟机:JVM高级特性与最佳实践。” iBooks.

当然了,就算不知道怎么估算,最终还是有神器Jprofile帮助我们监控性能与优化~

参考:

https://www.cnblogs.com/zhanjindong/p/3757767.html
https://blog.csdn.net/ITer_ZC/article/details/41822719
https://segmentfault.com/a/1190000006933272
https://www.jianshu.com/p/40faea07d4d2

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容