Java对象的内存布局:
`
对象头(Header)
实例数据(Instance Data)
对齐填充(Padding)`
###对齐填充
HotSpot的对齐方式为8字节对齐
(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
###指针压缩
```
对象占用的内存大小受到VM参数UseCompressedOops的影响。
```
| 项目 | 32 | 64 |
| --- | --- | --- |
||
| Header大小 | 8bytes | 16bytes |
|开启-XX:+UseCompressedOops)后Header的大小||12bytes|
|reference大小|4bytes|8bytes|
|开启-XX:+UseCompressedOops)后reference的大小||4bytes|
||||
如何测量一个类:
来自文章:
https://my.oschina.net/xianggao/blog/361584
```
package sizeof;
import java.lang.instrument.Instrumentation;
public class MySizeOf {
private static Instrumentation inst;
/**
*这个方法必须写,在agent调用时会被启用
*/
public static void premain(String agentArgs, Instrumentation instP) {
inst = instP;
}
/**
* 直接计算当前对象占用空间大小,包括:当前类及超类的基本类型实例字段大小
* 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小
* 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小
* 用来测量java对象的大小(这里先理解这个大小是正确的,后面再深化)
*/
public static long sizeOf(Object o) {
if(inst == null) {
throw new IllegalStateException("Can not access instrumentation environment.\n" +
"Please check if jar file containing SizeOfAgent class is \n" +
"specified in the java's \"-javaagent\" command line argument.");
}
return inst.getObjectSize(o);
}
}
```
在Idea上打包(jar包名为:MavenTest.jar)以后,MANIFEST.MF中(最后要留一行空的):
```
Manifest-Version: 1.0
Sealed: true
Premain-Class: sizeof.MySizeOf
```
TestSize.java:
```
import sizeof.*;
public class TestSize {
public static void main(String []args) {
System.out.println(MySizeOf.sizeOf(new Integer(1)));
System.out.println(MySizeOf.sizeOf(new String("a")));
System.out.println(MySizeOf.sizeOf(new char[1]));
}
}
```
![](https://box.kancloud.cn/6547acd9256a6628f2e1c5684baa6245_538x23.png)
![](https://box.kancloud.cn/006b7035e4b3f43c5c8f3f38ad1b55d7_487x68.png)
下一步准备运行,运行前我们准备初步估算下结果是什么,目前我是在32bit模式下运行jvm(注意,不同位数的JVM参数设置不一样,对象大小也不一样大)。
(1) 首先看Integer对象,在32bit模式下,_class区域占用4byte,_mark区域占用最少4byte,所以最少8byte头部,Integer内部有一个int类型的数据,占4个byte,所以此时为8+4=12,java默认要求按照8byte对象对其,所以对其到16byte,所以我们理论结果第一个应该是16;
(2)
再看String,长度为1,
String对象内部本身有4个非静态属性(静态属性我们不计算空间,因为所有对象都是共享一块空间的),
4个非静态属性中,有offset、count、hash为int类型,分别占用4个byte,
char value[]为一个指针,指针的大小在bit模式下或64bit开启指针压缩下默认为4byte,
所以属性占用了16byte,
String本身有8byte头部,所以占用了24byte;
-----------------------
(+0) | _mark |
-----------------------
(+4) | _metadata |
-----------------------
(+8) | value |
-----------------------
(+12)| offset |
-----------------------
(+16)| count |
-----------------------
(+20)| hash |
-----------------------
其次,一个String包含了子对象char数组,**数组对象和普通对象的区别是需要用一个字段来保存数组的长度**,所以头部变成12byte,java中一个char采用UTF-16编码,占用2个byte,所以是14byte,对其到16byte,24+16=40byte;
String在内存中的布局是:
(3) 第三个在第二个基础上已经分析,就是16byte大小;
也就是理论结果是:16、40、16;
步骤4:现在开始运行代码:运行代码前需要保证classpath把刚才的agent.jar包含进去: > D:>javac TestSize.java D:>java -javaagent:agent.jar TestSize 16 24 16
第一个和第三个结果一致了,不过奇怪了,第二个怎么是24,不是40,怎么和理论结果偏差这么大,再回到理论结果中,有一个24曾经出现过,24是指String而不包含char数组的空间大小,那么这么算还真是对的,可见,java默认提供的方法只能测量对象当前的大小,如果要测量这个对象实际的大小(也就是包含了子对象,那么就需要自己写算法来计算了,最简单的方法就是递归,不过递归一项是我不喜欢用的,无意中在一个地方看到有人用栈写了一个代码写得还不错,自己稍微改了下,就是下面这种了)。
---
然后参考:
http://yueyemaitian.iteye.com/blog/2033046
```
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
/**
* 对象占用字节大小工具类
*
* @author tianmai.fh
* @date 2014-03-18 11:29
*/
public class SizeOfObject {
static Instrumentation inst;
public static void premain(String args, Instrumentation instP) {
inst = instP;
}
/**
* 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
* 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
* 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
*
* @param obj
* @return
*/
public static long sizeOf(Object obj) {
return inst.getObjectSize(obj);
}
/**
* 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
*
* @param objP
* @return
* @throws IllegalAccessException
*/
public static long fullSizeOf(Object objP) throws IllegalAccessException {
Set<Object> visited = new HashSet<Object>();
Deque<Object> toBeQueue = new ArrayDeque<>();
toBeQueue.add(objP);
long size = 0L;
while (toBeQueue.size() > 0) {
Object obj = toBeQueue.poll();
//sizeOf的时候已经计基本类型和引用的长度,包括数组
size += skipObject(visited, obj) ? 0L : sizeOf(obj);
Class<?> tmpObjClass = obj.getClass();
if (tmpObjClass.isArray()) {
//[I , [F 基本类型名字长度是2
if (tmpObjClass.getName().length() > 2) {
for (int i = 0, len = Array.getLength(obj); i < len; i++) {
Object tmp = Array.get(obj, i);
if (tmp != null) {
//非基本类型需要深度遍历其对象
toBeQueue.add(Array.get(obj, i));
}
}
}
} else {
while (tmpObjClass != null) {
Field[] fields = tmpObjClass.getDeclaredFields();
for (Field field : fields) {
if (Modifier.isStatic(field.getModifiers()) //静态不计
|| field.getType().isPrimitive()) { //基本类型不重复计
continue;
}
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue == null) {
continue;
}
toBeQueue.add(fieldValue);
}
tmpObjClass = tmpObjClass.getSuperclass();
}
}
}
return size;
}
/**
* String.intern的对象不计;计算过的不计,也避免死循环
*
* @param visited
* @param obj
* @return
*/
static boolean skipObject(Set<Object> visited, Object obj) {
if (obj instanceof String && obj == ((String) obj).intern()) {
return true;
}
return visited.contains(obj);
}
}
```
大小计算实例:
```
import java.io.File;
import static com.tmall.buy.structure.SizeOfObject.*;
/**
* @author tianmai.fh
* @date 2014-03-18 20:17
*/
public class SizeOfObjectTest {
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
*/
static class A {
int a;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
*/
static class B {
int a;
int b;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32
*/
static class B2 {
int b2a;
Integer b2b;
}
/**
* 不考虑对象头:
* 4 + 4 + 4 * 3 + 3 * sizeOf(B)
*/
static class C extends A {
int ba;
B[] as = new B[3];
C() {
for (int i = 0; i < as.length; i++) {
as[i] = new B();
}
}
}
static class D extends B {
int da;
Integer[] di = new Integer[3];
}
/**
* 会算上A的实例字段
*/
static class E extends A {
int ea;
int eb;
}
public static void main(String[] args) throws IllegalAccessException {
System.out.println(new File("./target/classes").getAbsolutePath());
System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));
System.out.println("sizeOf(new A())=" + sizeOf(new A()));
System.out.println("sizeOf(new B())=" + sizeOf(new B()));
System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));
System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));
System.out.println("sizeOf(new C())=" + sizeOf(new C()));
System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));
System.out.println("sizeOf(new D())=" + sizeOf(new D()));
System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));
System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));
System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));
System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));
System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));
System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));
System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));
System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));
System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));
System.out.println("sizeOf(new E())=" + sizeOf(new E()));
}
}
```
文章的后续:
http://yueyemaitian.iteye.com/blog/2034305
将对象中的偏移量都打印出来。
```
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* -Xmx1024m
* @author tianmai.fh
* @date 2014-03-18 19:10
*/
public class FieldOffsetTest {
static Unsafe unsafe;
static {
Field field = null;
try {
field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe = (Unsafe) field.get(null);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
static class MyClass {
Object a = new Object();
Integer b = new Integer(3);
int c = 4;
long d = 5L;
Long[] e = new Long[2];
Object[] f = new String[0];
}
static class B2 {
int a;
Integer b;
int c;
}
static long objectFieldOffset(Field field) {
return unsafe.objectFieldOffset(field);
}
static String objectFieldOffset(Class<?> clazz) {
Field[] fields = clazz.getDeclaredFields();
StringBuilder sb = new StringBuilder(fields.length * 50);
sb.append(clazz.getName()).append(" Field offset:\n");
for (Field field : fields) {
sb.append("\t").append(field.getType().getSimpleName());
sb.append("\t").append(field.getName()).append(": ");
sb.append(objectFieldOffset(field)).append("\n");
}
return sb.toString();
}
public static void main(String[] args) throws InterruptedException, NoSuchFieldException {
MyClass mc = new MyClass();
int[] big = new int[30 * 1024 * 1024];
big = null;
System.gc();
System.out.println(objectFieldOffset((MyClass.class)));
System.out.println(objectFieldOffset((B2.class)));
Object a = new Long[1];
System.out.println(Long[].class.getName());
Thread.sleep(1000000);
}
}
```
另一篇文章:
http://www.open-open.com/lib/view/open1423111722764.html
下面是一个大神的扩展阅读:
http://rednaxelafx.iteye.com/blog/730461
- 参考资料
- 容器的实现
- ArrayList、LinkedList与Vector的区别
- Map,Set,List,Queue,Stack的特点与用法
- HashMap的实现
- HashMap和ConcurrentHashMap差别
- HashMap和HashTable的区别
- fast fail
- java 实用方法
- Collections中实用的函数
- ArrayList中实用的函数
- Integer和Character
- Properties类的简单使用
- XML实用解析
- 从jar包中读取文件信息
- java自带base64加密解密
- java机制
- 分派
- 反射
- 类加载机制
- java中一个对象的初始化
- 泛型
- 自动装箱,拆箱与遍历循环
- 偏向于语法
- new int[]
- new boolean[]
- Switch能否用string做参数
- equals与==的区别
- 泛型对象数组
- Enum的用法
- String、StringBuffer与StringBuilder的区别
- try catch finally
- finalize方法
- object有哪些公用方法
- Java的四种引用,强弱软虚,用到的场景
- java访问修饰符
- Hashcode的作用
- 九种基本数据类型
- java对象大小
- 数组长度
- 动态代理的一个例子
- java.lang.NoClassDefFoundError
- ThreadLocal