零、注记
本文是一次讨论的流水账,旨在讲明原理就行了,行文大家不要抱太大的希望。
另外,特别重要的是,本文是基于hotspot来讨论的,不同的java虚拟机可能是有不同的,这一点,一定要注意。
一、什么是对象的内存布局
简单一句话:对象实例在jvm堆内存中存放的结构。就是随便实例化一个对象new Object(),他在堆内存里面是怎么放置的。
看下面这个jol工具给出的java.math.BigInteger内存布局的例子:一个对象的内存布局包含了对象头object header、实例数据域和对齐填充alignment padding(可能有,可能没有,下面再细说)。
***** 64-bit VM, compressed references enabled: ***************************
java.math.BigInteger object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int BigInteger.signum N/A
16 4 int[] BigInteger.mag N/A
20 4 int BigInteger.bitCount N/A
24 4 int BigInteger.bitLength N/A
28 4 int BigInteger.lowestSetBit N/A
32 4 int BigInteger.firstNonzeroIntNum N/A
36 4 (loss due to the next object alignment)
Instance size: 40 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
二、查看对象内存布局的工具
1. openjdk jol
openjdk官网给了一个查看对象内存布局的工具,jol(java object layout):http://openjdk.java.net/projects/code-tools/jol/
怎么拿呢?openjdk给了maven的依赖:
Use as Library Dependency
OpenJDK Community semi-regularly pushes the releases to Maven Central. Therefore, you can use it right away by setting up the Maven dependency:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>put-the-version-here</version>
</dependency>
It is a good idea to review JOL Samples and CLI tools source before using the tool at its full capacity as the library.
怎么用呢?上面给的jol的链接页面,最下面官方给了jol samples的链接,使用极其简单,就是一个ClassLayout就没了。示例就懒得给了,看samples吧。
jol samples:http://hg.openjdk.java.net/code-tools/jol/file/tip/jol-samples/src/main/java/org/openjdk/jol/samples/
jol sourcecode:http://central.maven.org/maven2/org/openjdk/jol/
如果不想看samples呢?这篇参考文章给了用例和讲解《JDK之JVM中Java对象的头部占多少byte》:https://my.oschina.net/u/2518341/blog/1838006
那如果不想用jol工具怎么办呢?卧槽,我好难啊。。。
2. sun.misc.Unsafe
- sun.misc.Unsafe.objectFieldOffset方法获取第一个field的偏移地址(弊端:当对象头后面有padding的时候,你看不出来,什么时候有padding呢,下面会细说)
- unsafe怎么用呢?《Understanding sun.misc.Unsafe》:https://dzone.com/articles/understanding-sunmiscunsafe
- JDK8及之前,是用的sun.misc.Unsafe
- JDK9有两个Unsafe,除了sun.misc.Unsafe还提供了jdk.internal.misc.Unsafe,但是jdk.internal.misc.Unsafe不像sun.misc.Unsafe是可以通过反射使用的,实际上目前在JDK9以后的版本中,sun.misc.Unsafe中组合了jdk.internal.misc.Unsafe的实例,实际上sun.misc.Unsafe是一个简单包装,你可以自己翻翻源码。
至少有两种方式可以获取到sun.misc.Unsafe实例对象:
- 通过反射sun.misc.Unsafe的构造函数获取其实例对象;
- 通过反射sun.misc.Unsafe的实例属性theUnsafe获取其实例对象;
package cn.wxy.unsafe;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import sun.misc.Unsafe;
public class UnsafeUtils {
/**
* 通过反射sun.misc.Unsafe的构造函数获取其实例对象
*
* @return sun.misc.Unsafe
*/
public static Unsafe getUnsafeByConstructor() {
Constructor<Unsafe> constructor = null;
try {
constructor = Unsafe.class.getDeclaredConstructor();
const