目录
JAVA对象布局
上图是堆中的Java对象的布局
Java对象由对象头和对象体组成,对了,最后面还会有个8字节按需对齐
对象头由Mark Word和Klass Pointer组成以及数组长度组成
数组长度只有对象是数组才会有值
Klass Pointer指向对象的Class信息
Mark Word 存储对象自身的一些数据,如锁信息、hashCode、gc年龄等
maven引入
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
JOL初体验
package pers.wmx.springbootfreemarkerdemo.jol;
import org.openjdk.jol.info.ClassLayout;
/**
* JOL(Java Object Layout)
* 查看对象的内存布局、内存踪迹和引用
*
* @author wangmingxin03
* Created on 2021-11-12
*/
public class Main1 {
public static void main(String[] args) {
A a = new A();
System.out.println("a : " + ClassLayout.parseInstance(a).toPrintable());
}
static class A {
private int x = 10;
}
}
关键方法 ClassLayout.parseInstance(a).toPrintable()
a : pers.wmx.springbootfreemarkerdemo.jol.Main1$A 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 A.x 10
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
看到objetc header 12字节,12 * 8 = 96bit
对象头占据12字节,大家可能看到一些文章写的是16字节 16 * 8 = 128bit
因为新版Jdk默认开启了指针压缩,所以是96bit
|--------------------------------------------------------------|
| Object Header (96 bits) |
|------------------------------------|-------------------------|
| Mark Word (64 bits) | Klass pointer (32 bits) |
|------------------------------------|-------------------------|
可以关闭指针压缩 -XX:-UseCompressedOops
再run一下
a : pers.wmx.springbootfreemarkerdemo.jol.Main1$A 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) a0 a5 15 1d (10100000 10100101 00010101 00011101) (487957920)
12 4 (object header) 02 00 00 00 (00000010 00000000 00000000 00000000) (2)
16 4 int A.x 10
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
发现对象头16个字节了
|--------------------------------------------------------------|
| Object Header (128 bits) |
|------------------------------------|-------------------------|
| Mark Word (64 bits) | Klass pointer (64 bits) |
|------------------------------------|-------------------------|
- OFFSET:偏移地址,单位字节;
- SIZE:占用的内存大小,单位为字节;
- TYPE DESCRIPTION:类型描述,其中
object header
为对象头; - VALUE:对应内存中当前存储的值;
从锁升级看对象头
锁升级的细节,本文不做详细的介绍,太复杂了,其实我也没太搞明白,简单通过demo看下锁升级和认识下对象头
package pers.wmx.springbootfreemarkerdemo.jol;
import java.util.concurrent.TimeUnit;
import org.openjdk.jol.info.ClassLayout;
/**
* JOL(Java Object Layout)
* 查看对象的内存布局、内存踪迹和引用
*
* @author wangmingxin03
* Created on 2021-11-12
*/
public class Main {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable()); // 001 (无锁)
synchronized (o) {
System.out.println(ClassLayout.parseInstance(o).toPrintable()); // 000 (轻量级锁)
}
Thread t1 = new Thread(() -> {
synchronized (o) {
System.out.println("t1 lock " + Thread.currentThread().getName());
System.out.println(ClassLayout.parseInstance(o).toPrintable()); // 010 (重量级锁)
}
});
Thread t2 = new Thread(() -> {
synchronized (o) {
System.out.println("t2 lock " + Thread.currentThread().getName());
System.out.println(ClassLayout.parseInstance(o).toPrintable()); // 010 (重量级锁)
}
});
t1.start();
t2.start();
Thread.sleep(TimeUnit.SECONDS.toMillis(5));
// 线程都执行完,释放锁
System.out.println("after thread lock : " + ClassLayout.parseInstance(o).toPrintable()); // 001 (无锁)
}
}
上面给了简单的demo, 对象一开始创建是无锁
一个线程加锁到轻量锁
多线程竞争加锁到重量锁
从后往前看,看最后三位 001 表示无锁
在底层源码中有这样的一个枚举定义
enum { locked_value = 0, // 0 00 轻量级锁
unlocked_value = 1,// 0 01 无锁
monitor_value = 2,// 0 10 重量级锁
marked_value = 3,// 0 11 gc标志
biased_lock_pattern = 5 // 1 01 偏向锁
};
这64bit在锁升级过程中位的分配还有不同
上图可以看到当锁升级到轻量级锁,mark word存储的变成了争抢到锁的线程的线程栈中的锁记录(Lock Record)的指针了
mark word存储的是哪个线程的lock record,就是哪个线程抢到了锁
肯能有兄弟要问了,那以前的那些信息存哪去了?Lock Record会存储指向之前状态的对象的mark ward信息的指针
转载请注明:汪明鑫的个人博客 » 介绍一种Java对象布局小工具
说点什么
您将是第一位评论人!