原文地址:http://flychao88.iteye.com/blog/1913353
原文如下:
这里介绍java常用对象的内存占用大小.32位和64位以及不同的jvm参数可能会导致对象大小不同.
java版本
Open JDK 64-bit
jvm参数
-Xmx2g -Xms2g
基本类型
对象 32位大小
new Object() 16
new String("") 48
new String(new char[] {'1'}) 56
new String(new char[] {'1','2','3','4'}) 56
new String(new char[] {'1','2','3','4','5'}) 64
new Integer(0) 16
new Long(0) 24
new Float(0) 16
new Double(0) 24
new BigDecimal(0) 40
new Date() 24
new java.sql.Timestamp() 32
UUID 64
GregorianCalendar 448
new AtomicBoolean() 16
new AtomicInteger() 16
new AtomicLong() 24
容器
对象 大小
new TreeMap() 48
new HashMap() 136
new HashMap(1) 80
new Hashtable() 120
new Properties() 120
new LinkedHashMap() 120
new WeakHashMap() 144
new ConcurrentSkipListMap() 104
new ConcurrentHashMap() 224
new ConcurrentHashMap(1,0.75f,1) 168
new TreeSet() 64
new HashSet() 160
new LinkedHashSet() 200
new ArrayList() 80
new ArrayList(1) 48
new CopyOnWriteArrayList() 88
new CopyOnWriteArraySet() 104
new Vector() 88
new LinkedList() 32
new Stack() 88
new PriorityQueue() 96
new ConcurrentLinkedQueue() 48
数组
对象 大小
new byte[0] 16
new byte[1] 24
new byte[8] 24
new short[0] 16
new short[1] 24
new short[4] 24
new short[5] 32
new char[0] 16
new char[1] 24
new char[4] 24
new char[5] 32
new int[0] 16
new int[1] 24
new int[2] 24
new int[3] 32
new long[0] 16
new long[1] 24
new long[2] 32
new Object[0] 16
new Object[1] 24
new Object[2] 24
new Object[3] 32
********************原文结束,学习笔记开始*********************************
上面只是简单列举。那么这数据怎么来的呢?要分析下:
参考文章
1
原生类型(primitive type)的内存占用
Primitive Type Memory Required(bytes)
—————————————————————
boolean 1
byte 1
short 2
char 2
int 4
float 4
long 8
double 8
Java对象的内存布局:对象头(Header),实例数据(Instance Data)和对齐填充(Padding)
第一部分用于存储对象自身的运行时数据, 如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,这部分数据的长度在32位和64位的虚拟机(暂不考虑开启压缩指针的场景)中分别为32个和64个Bits,官方称它为“Mark Word”。这部分跟之前介绍同步锁信息有提到过。
1. 一个object header, 也称object overhead, 保存当前实例的type信息和内置monitor信息等, 32位系统上占用8bytes,64位系统上占用16bytes;
2.
0到多个fields, reference类型在32位系统上每个占用4bytes, 在64位系统上每个占用8bytes; primitive类型参考上面;
3(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
开始举例验证下上面文章所列举的数据:
new byte[1] 24
new byte[8] 24
在32位的系统中:
型别占用内存 * 数组长度 + 8(数组在JVM中被当成特殊的对象, object overhead占用8bytes) + 4(数组长度) + padding。所以上述应该为:
new byte[1]
1*1+8+4=13,+padding(3)= 16.
new byte[8]
8*1 +8+4=20,+padding(4)=24
在
64位的系统中, 占用内存为: 型别占用内存 * 数组长度 + 16(object overhead占用16bytes) + 8(数组长度) + padding。
计算如下:
new byte[1]
1*1+16+8=25+padding(7)=32,
new byte[2]
2*1+16+8=26+padding(8)=32,
new byte[8]
8*1+16+8=32,
所以上面文章写的是不正确的。下面用代码测试下。
详情参照大神文章:http://yueyemaitian.iteye.com/blog/2033046
从jdk5开始,提供了Instrumentation API,它有一个叫做getObjectSize()的方法,但是,这个方法存在问题:不可以直接使用。必须要实现一个Instrumentation Agent,还得放到jar包里面。解决方案:在任何一个类里面声明一个"premain"方法,就可以把这个类做成是一个agent,jvm在启动的时候会调用premain()方法,同时会传递Instrumentation这个对象实例,要告诉jvm Instrumentation agent所在的类,需要把这个类打到jar包里面。
另外,在打包时候需要在MANIFEST.MF中写入三项值(注意包路径名改成自己的包名):
- Premain-class: xxx.yyy.zzz.SizeOfObject
- Can-Redefine-Classes: false
- Boot-Class-Path:
代码就是用的大神提供的sizeof方法。测试代码比较简单:
public class SizeTest {
public static void main(String[] args) {
byte[] b = new byte[2];
System.out.println("object:"+ SizeOfObject.sizeOf(new Object()));
System.out.println( "byte[2]"+SizeOfObject.sizeOf(b));
}
}
下面写一下操作步骤,注意一点关于
MANIFEST.MF,如果这里面不指定主类,需要在执行java命令时写明执行类
:
进入命令行,查看版本:
从Java SE 6u23之后的64位版本就默认打开了对象指针压缩。所以本机1.7也是,注意对比结果不同下面会有分析。
进度测试代码目录,并执行编译:javac *.java
打包,jar -cvfm size.jar MANIFEST.MF *
执行,分别对应启动指针压缩,关闭指针压缩。
分析不同:下面未开启情况: new byte[2]2*1+16+8=26+padding(8)=32,
针对开启情况: object header,未压缩前由一个native-sized mark word 8bytes加上一个class word 8bytes组成,共16bytes。采用压缩后,class word缩减为4bytes,现共占用12bytes;
2. reference类型,由8bytes缩减为4bytes;
3. 数组长度,由8bytes缩减为4bytes。
再算下压缩后情况:
new byte[2]
2*1+12+4=18+padding(6)=24
看完了一维数组,看下多维数组
1. 在32位的系统中, 占用内存为: reference占用内存 * 数组第1维长度 +12(数组本身被当做reference占8bytes,数组长度占4bytes)。如:
byte[3][7], reference占用内存4byte,数组第1维长度为3,这样占用的总内存为4 * 3 + 12 = 24,所以byte[3][7]占用内存为24bytes。再如byte[7][3], reference占用内存4byte,数组第1维长度为7,这样占用的总内存为4 * 7 + 12 = 40,所以byte[7][3]占用内存为40bytes。
2. 在64位的系统中, 占用内存为: reference占用内存 * 数组第1维长度 +24(数组本身被当做reference占16bytes,数组长度占8bytes)。如:
byte[3][7], reference占用内存8byte,数组第1维长度为3,这样占用的总内存为8 * 3 + 24 = 48,所以byte[3][7]占用内存为48bytes。
复合对象计算麻烦些,
包括当前类和超类的实例字段大小以及实例字段引用对象大小。
************************学习笔记总结*******************************
1.同样的程序在不同环境下占用内存大小不一样,64位比32位大。
2,多维数组优化内存角度考虑:数组的一维尽可能小,这样占的内存小。
3.jdk1.6以后默认开启内存指针压缩,占的内存比以前未开启的要小。
就到这里吧,在考虑调优应该是jvm堆、栈那块知识了。另外整理吧。