java并发基础-01

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并发包的基石。