java对象内存布局
首先要明确的是java对象大小必须是8的倍数,对象头占12字节
java 对象分为三部分:对象头(12字节)、实例数据、对齐填充(保证整个对象大小是8的倍数)
openJDK有个工具包,可以打印对象的内存布局:
1 2 3 4 5
| <dependency> <groupId>org.openjdk.jol</groupId> <artifactId>jol-core</artifactId> <version>0.10</version> </dependency>
|
打印java对象内存布局:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Mao {
boolean flag;
int a; }
public class TestMao {
public static void main(String[] args) { Mao mao = new Mao(); System.out.println(ClassLayout.parseInstance(mao).toPrintable()); } }
|
结果:
1 2 3 4 5 6 7 8 9 10
| learn.Mao object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253) 12 4 int Mao.a 0 16 1 boolean Mao.flag false 17 7 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 7 bytes external = 7 bytes total
|
可以看到jvm为了保证对象大小是8的倍数,使用了7个字节来填充。
Unsafe 魔法类
R大的官方解释:Unsafe是用于在实质上扩展Java语言表达能力、便于在更高层(Java层)代码里实现原本要在更低层(C层)实现的核心库功能用的。这些功能包括裸内存的申请/释放/访问,低层硬件的atomic/volatile支持,创建未初始化对象等。它原本的设计就只应该被标准库使用。
Unsafe类不能被直接new出来使用,原因是其构造方法是私有的,Unsafe的初始化方法主要是通过getUnsafe方法的单例模式实现,在getUnsafe方法里限定了只有BootStrap classLoader 才能对其进行加载,否则抛出SecurityException
1 2 3 4 5 6 7 8 9 10 11 12 13
| private Unsafe() { }
private static final Unsafe theUnsafe; public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
|
使用Unsafe的方法,使用反射:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UnsafeInstance {
public static Unsafe reflectGetUnsafe() { try { Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); return (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } return null; } }
|
Unsafe是java并发包的基石。