计算对象大小

参考:
http://article.yeeyan.org/view/104091/62930?from_com

http://www.ibm.com/developerworks/cn/java/j-lo-optmizestring/index.html

http://www.ibm.com/developerworks/cn/java/j-codetoheap/index.html(好贴)

1 Java对象 

一般而言,Java 对象在虚拟机的结构如下:

  • 对象头(object header):8 个字节
  • Java 原始类型数据:如 int, float, char 等类型的数据,各类型数据占内存如 表 1. Java 各数据类型所占内存.
  • 引用(reference):4 个字节
  • 填充符(padding)

表 1. Java 各数据类型所占内存

数据类型 占用内存(字节数)
boolean 1
byte
char 2
short
int 4
float
long 8
double

然而,一个 Java 对象实际还会占用些额外的空间,存储对象元数据,如:对象的 class 信息、ID、在虚拟机中的状态。在 Oracle JDK 的 Hotspot 虚拟机中,一个普通的对象需要额外 8 个字节。

这些元数据通常如下:

  • :一个指向类信息的指针,描述了对象类型。举例来说,对于 java.lang.Integer 对象,这是 java.lang.Integer 类的一个指针。
  • 标记:一组标记,描述了对象的状态,包括对象的散列码(如果有),以及对象的形状(也就是说,对象是否是数组)。
  • :对象的同步信息,也就是说,对象目前是否正在同步。

加入在32位机器,创建java.lang.Integer对象,对象内存布局如下:

一个 32 位 Java 进程的 java.lang.Integer 对象的布局示例

注意,其中前96位都是存储元数据,只有后面32位存储真实的int值。

2 Java数组对象

仍然以int型数组(注意不是Integer型数组)为例:

一个 32 位进程的 int 数组对象的布局示例

如上图,元数据多了一个size,描述数组的大小。size之后,用于存储诸多int

3 String类型

如果对于 String(JDK 6)的成员变量声明如下:

  private final char value[]; 
  private final int offset; 
  private final int count; 
  private int hash; 		

一个 32 位 Java 进程的 java.lang.String 对象的布局示例

这也就意味着,对于一个 8 个字符的字符串(128 位的 char 数据),需要有 256 位的数据用于字符数组,224 位的数据用于管理该数组的java.lang.String 对象,因此为了表示 128 位(16 个字节)的数据,总共需要占用 480 位(60 字节)。开销比例为 3.75:1。总体而言,数据结构越是复杂,开销就越高。

那么因该如何计算该 String 所占的空间?首先计算一个空的 char 数组所占空间,在 Java 里数组也是对象,因而数组也有对象头,故一个数组所占的空间为对象头所占的空间加上数组长度,即 8 + 4 = 12 字节 , 经过填充后为 16 字节。那么一个空 String 所占空间为:

 对象头(8 字节)+ char 数组(16 字节)+ 3 个 int(3 × 4 = 12 字节)+1 个 char 数组的引用 (4 字节 ) = 40 字节。

因此一个实际的 String 所占空间的计算公式如下:

8*( ( 8+2*n+4+12)+7 ) / 8 = 8*(int) ( ( ( (n) *2 )+43) /8 );其中,n 为字符串长度。

4 从32位到64位

4.1 内存增长

上面讨论的都是在32位场景下,但是到了64位,这些对象元数据都会增长到64位,但存储的真实值数据却不会增长,客观导致浪费。如下图:

一个 64 位 Java 进程的 java.lang.Integer 对象和 int 数组的布局示例

对于一个 64 位 Integer 对象,现在有 224 位的数据用于存储 int 字段所用的 32 位,开销比例是 7:1。对于一个 64 位单元素int 数组,有 288 位的数据用于存储 32 位 int 条目,开销比例是 9:1。这在实际应用程序中产生的影响在于,之前在 32 位 Java 运行时中运行的应用程序若迁移到 64 位 Java 运行时,其 Java 堆内存使用量会显著增加。通常情况下,增加的数量是原始堆大小的 70% 左右。举例来说,一个在 32 位 Java 运行时中使用 1GB Java 堆的 Java 应用程序在迁移到 64 位 Java 运行时之后,通常需要使用 1.7GB 的 Java 堆。这种内存增加并非仅限于 Java 堆。本机堆内存区使用量也会增加,有时甚至要增加 90% 之多。

4.2 各种类型对比

这个表是从参考3粘过来的,个人表示看不懂。。。。

字段类型 字段大小(位)
对象 数组
32 位 64 位 32 位 64 位
boolean 32 32 8 8
byte 32 32 8 8
char 32 32 16 16
short 32 32 16 16
int 32 32 32 32
float 32 32 32 32
long 32 32 64 64
double 32 32 64 64
对象字段 32 64 (32*) 32 64 (32*)
对象元数据 32 64 (32*) 32 64 (32*)

* 对象字段的大小以及用于各对象元数据条目的数据的大小可通过 压缩引用或压缩 OOP 技术减小到 32 位。

4.3 压缩引用和压缩普通对象指针 (OOP)

IBM 和 Oracle JVM 分别通过压缩引用 (-Xcompressedrefs) 和压缩 OOP (-XX:+UseCompressedOops) 选项提供对象引用压缩功能。利用这些选项,即可在 32 位(而非 64 位)中存储对象字段和对象元数据值。在应用程序从 32 位 Java 运行时迁移到 64 位 Java 运行时的时候,这能消除 Java 堆内存使用量增加 70% 的负面影响。请注意,这些选项对于本机堆的内存使用无效,本机堆在 64 位 Java 运行时中的内存使用量仍然比 32 位 Java 运行时中的使用量高得多。

5 集合内存使用

最看不懂却最重要的一节!!!

.......

3 常见方法

http://jroller.com/page/mipsJava?entry=sizeof_java_objects -

使用System.gc(),Runtime.freeMemory(), Runtime.totalMemory()方法来计算java对象的大小。这个方法通常需要许多资源才能精确计算出对象的大小。它必须创建许多的需要估算对象的实例(最好是几千个),在创建的前后量测堆内存的大小。这个方法对于使用缓存机制的生产系统并不奏效。这个方法的优点是可以得到较为精确的结果,而不受Java实现版本和操作系统的影响。

http://www.javaspecialists.co.za/archive/Issue078.html - 

他使用真实的原始类型大小的对照表来确定整个对象的大小。使用反射API遍历对象继承链上的成员变量并且计算所有原始类型变量的大小。这个方法不像上一方法那样需要很多的资源并能够用于缓存机制。弊端是原始类型大小的对照表会随着JVM实现版本的不同而不同,对于不同的实现版本需要重新计算。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值