研究java对象头,我这里先截取Hotspot中关于对象头的描述,本文研究基于64-bit HotSpot VM
文件路径 openjdk-jdk8u-jdk8u\hotspot\src\share\vm\oops\markOop.hpp
// 32 bits:
// --------
// hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
// size:32 ------------------------------------------>| (CMS free block)
// PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)
//
// 64 bits:
// --------
// unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object)
// JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object)
// PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object)
// size:64 ----------------------------------------------------->| (CMS free block)
//
// unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object)
// JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object)
// narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object)
// unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free block)
一:java对象组成 : 对象头(mark word 和 klass Pointer,如果是数组,包含数组长度)、实例化数据、对齐填充
借助openJdk中的工具类JOL来分析java对象布局
maven的pom.xml中添加依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
Test1:
public class A {
boolean flag = false ;
}
------------------------------------------------------------
public class Current{
public static void main(String[] args) {
A obj = new A();
A[] b = new A[10];
System.out.println(VM.current().details());
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
System.out.println(ClassLayout.parseInstance(b).toPrintable());
}
}
运行结果:
# Running 64-bit HotSpot VM.
# Using compressed oop with 0-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]com.yew.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 a1 f9 27 (01000011 10100001 11111001 00100111) (670671171)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total[Lcom.yew.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) 81 a1 f9 27 (10000001 10100001 11111001 00100111) (670671233)
12 4 (object header) 0a 00 00 00 (00001010 00000000 00000000 00000000) (10)
16 40 com.yew.A A;.<elements> N/A
Instance size: 56 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Test1分析:普通对象总共16B,对象头占用12B,另外4B(64位虚拟机上对象的大小必须是8的倍数)为对齐填充字节。
Hotspot中的重要术语:
mark word
The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.
每个对象头的第一个word。通常是一组位字段,包括同步状态和标识哈希值。也可以是指向同步相关信息的指针(具有特征性的低位编码)。在GC期间,可以包含GC状态位
klass pointer
The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the "klass" contains a C++ style "vtable".
klass word为对象头的第二个word,主要指向对象的元数据;
object header
Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object's layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM-internal objects have a common object header format.
对象头由两个word组成,每个GC管理堆对象的开头都存在这样一个公共结构。(每个oop都指向一个对象头)包含有关堆对象的布局、类型、GC状态、同步状态和哈希值的基本信息。在数组中,后面紧接着是一个长度字段。注意Java对象和VM内部对象都有一个通用的对象头格式。
二:java对象头
64位对象头由Mark Word、klass pointer两部分组成,如果对象是数组,则还要加上数组长度,即三部分组成。
Mark Word由64位8个字节组成,klass pointer由64位8个字节组成,但我们使用的64位 JVM会默认使用选项 +UseCompressedOops 开启指针压缩,将指针压缩至32位。即上面截图中的klass pointer为4个字节32位。这里重点描述下Mark Word部分。
Mark Word的64位,不同的位表示的意思不一样,具体如下所示:
|--------------------------------------------------------------------------------------------------------------|
| Object Header (128 bits) |
|--------------------------------------------------------------------------------------------------------------|
| Mark Word (64 bits) | Klass Word (64 bits) |默认开启指针压缩
|--------------------------------------------------------------------------------------------------------------|
|unused:25|identity_hashcode:31(56) | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 无锁
|----------------------------------------------------------------------|--------|------------------------------|
| thread:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | lock:2 | OOP to metadata object | 偏向锁
|----------------------------------------------------------------------|--------|------------------------------|
| ptr_to_lock_record:62 | lock:2 | OOP to metadata object | 轻量锁
|----------------------------------------------------------------------|--------|------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:2 | OOP to metadata object | 重量锁
|----------------------------------------------------------------------|--------|------------------------------|
| | lock:2 | OOP to metadata object | GC
|--------------------------------------------------------------------------------------------------------------|
对象在无锁的情况下,前56位为对象的hashCode值,下面为验证代码Test2:
public class Current{
public static void main(String[] args) throws Exception {
A obj = new A();
out.println("before hash");
out.println(ClassLayout.parseInstance(obj).toPrintable());
out.println("JVM hash==="+Integer.toHexString(obj.hashCode()));
out.println("after hash");
countHash(obj);
out.println(ClassLayout.parseInstance(obj).toPrintable());
}
public static void countHash(Object obj) throws Exception {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
long hashCode = 0;
for (long index = 7; index > 0; index--) {
hashCode |= (unsafe.getByte(obj, index) & 0xFF) << ((index-1) * 8);
}
String code = Long.toHexString(hashCode);
out.println("util 0x===="+code);
}
}
执行结果:
before hash
com.yew.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 21 ed 27 (01000011 00100001 11101101 00100111) (669851971)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
JVM hash===368102c8
after hash
util 0x====368102c8
很明显:此处得到的值与对象的hashCode值相等
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 c8 02 81 (00000001 11001000 00000010 10000001) (-2130524159)
4 4 (object header) 36 00 00 00 (00110110 00000000 00000000 00000000) (54)
8 4 (object header) 43 21 ed 27 (01000011 00100001 11101101 00100111) (669851971)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
Test2分析:因为对象字节采用小端存储,所以无锁状态2-7字节存放的是对象的Hashcode值,那么第一个字节8位存放的是分代年龄、偏向锁信息,和对象状态
2.1 无锁
public class NoLock {
public static void main(String[] args) {
A a = new A();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
a.hashCode();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
执行结果:
com.yew.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 21 f6 27 (01000011 00100001 11110110 00100111) (670441795)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes totalcom.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 c8 02 81 (00000001 11001000 00000010 10000001) (-2130524159)
4 4 (object header) 36 00 00 00 (00110110 00000000 00000000 00000000) (54)
8 4 (object header) 43 21 f6 27 (01000011 00100001 11110110 00100111) (670441795)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
分析结果:当一个对象才new且调用了hashcode方法后(如果不调用hashcode方法,那么存放hashcode的31位全部为0),正常情况下处于无锁状态,无锁状态时,Mark Word的64位分别为:前25位未使用,接下来的31位为对象的hashcode,接下来的1位未使用,接下来的4位表示对象的GC年龄,接下来的一位为偏向锁状态,最后2位表示锁状态,如上图所示。
2.2 偏向锁
偏向锁:1.保证线程安全;2.不存在资源竞争
public class BiasedLock {
public static void main(String[] args) /**throws InterruptedException**/ {
// Thread.sleep(4000);
// Thread.sleep(5000);
A a = new A();
System.out.println("before lock");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
synchronized (a){
System.out.println("locking...........");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
System.out.println("after lock");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
执行结果如下:
before lock
com.yew.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 fa 27 (01000011 11000001 11111010 00100111) (670744899)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes totallocking...........
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) e8 f0 6c 02 (11101000 11110000 01101100 00000010) (40693992)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 fa 27 (01000011 11000001 11111010 00100111) (670744899)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes totalafter lock
com.yew.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 fa 27 (01000011 11000001 11111010 00100111) (670744899)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
测试分析:根据执行结果分析,对象a并没有成为偏向锁,而是由 无锁-轻量锁-无锁。因为上述案例仅有一个线程调用synchronized,预期为偏向锁,但是实际运行与预期不符,根据查询资料发现,虚拟机在启动的时候会延迟开启偏向锁(时间约为4300ms),便于测试,可以通过设置JVM参数关闭偏向锁的延迟加载。IDEA设置方式:Run-->Edit Configurations->VM Options -XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0,再次运行可得结果。
before lock
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 fd 27 (01000011 11100001 11111101 00100111) (670949699)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
此时占用 thread 和 epoch 的 位置的均为0,说明当前偏向锁并没有偏向任何线程。此时这个偏向锁正处于可偏向状态,准备好进行偏向了!你也可以理解为此时的偏向锁是一个特殊状态的无锁
locking...........
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 f0 27 02 (00000101 11110000 00100111 00000010) (36171781)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 fd 27 (01000011 11100001 11111101 00100111) (670949699)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
synchronized(a),对a加锁后,对象a偏向了某个线程,所以2-8位表示的是线程的ID标识,所以显然此时对象a已经成为偏向锁。
after lock
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 f0 27 02 (00000101 11110000 00100111 00000010) (36171781)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 fd 27 (01000011 11100001 11111101 00100111) (670949699)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
退出同步代码块,依然是偏向锁
睡眠5s或者关闭偏向锁延迟加载时 偏向锁(可偏向)--->偏向锁(偏向线程)--->偏向锁
2.3 轻量锁
public class LightLock {
public static void main(String[] args) {
A a = new A();
synchronized (a){
System.out.println("locking......");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
}
locking......
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 28 f4 ef 02 (00101000 11110100 11101111 00000010) (49280040)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 f0 27 (01000011 11100001 11110000 00100111) (670097731)
12 1 boolean A.flag false
13 3 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 3 bytes external = 3 bytes total
结果分析:JVM默认开启偏向锁延迟加载,所以当使用synchronized给对象加锁时,该对象会膨胀为轻量锁。
2.4 重量锁
重量锁:当存在线程竞争同一把锁时,就会由轻量锁膨胀为重量级锁;
public class WeightLock {
static A a = new A();
public static void main(String[] args) throws InterruptedException {
System.out.println("before lock");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//无锁
new Thread(new Runnable(){
@Override
public void run() {
synchronized (a){
System.out.println("once locking");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread release");
}
}
}).start();
Thread.sleep(1000);
System.out.println("main printLock");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
synchronized (a){
System.out.println("second locking");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//重量锁
}
System.out.println("after locking");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//重量锁
System.gc();
System.out.println("after gc");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//gc标记
}
}
执行结果:
before lock
com.yew.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) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalonce locking
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) b0 f1 e2 0b (10110000 11110001 11100010 00001011) (199422384)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalmain printLock
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) b0 f1 e2 0b (10110000 11110001 11100010 00001011) (199422384)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalthread release
second locking
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ca 9f ac 08 (11001010 10011111 10101100 00001000) (145530826)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalafter locking
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) ca 9f ac 08 (11001010 10011111 10101100 00001000) (145530826)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalafter gc
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 09 00 00 00 (00001001 00000000 00000000 00000000) (9)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 18 0a 07 1e (00011000 00001010 00000111 00011110) (503777816)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
Biased_lock(1bits) | Lock(2bits) | description |
0 | 01 | 无锁 |
1 | 01 | 偏向锁 |
0 | 00 | 轻量锁 |
0 | 10 | 重量锁 |
0 | 01 | 无锁--GC标记 |
三:偏向锁/轻量级锁/重量级锁
上述三种锁是指锁的状态,并且是针对Synchronized。通过引入锁升级的机制来实现高效Synchronized。锁的状态是通过对象监视器在对象头中的字段来表明的。
偏向锁:一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁:当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁:当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让它申请的线程进入阻塞,性能降低。
自旋锁:自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
三:性能比较
3.1 偏向锁和轻量锁的性能对比
//偏向锁 关闭偏向锁延迟加载
//VM Options -XX:BiasedLockingStartupDelay=0
@Test
public void testBiasedLock(){
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000 ; i++) {
synchronized (a){
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("耗时为:"+(end-start)+"ms");
System.out.println(count);
}
//轻量锁
@Test
public void testLightLock(){
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000000 ; i++) {
synchronized (a){
count++;
}
}
long end = System.currentTimeMillis();
System.out.println("耗时为:"+(end-start)+"ms");//36742ms
System.out.println(count);
}
//重量锁
@Test
public void testWeightLock(){
A a = new A();
long start = System.currentTimeMillis();
for (int i = 0; i < 2; i++) {
new Thread(a::parse, "t" + i).start();
}
System.out.println(Thread.activeCount());
while (Thread.activeCount()>2){
Thread.yield();
}
long end = System.currentTimeMillis();
System.out.println("耗时为:"+(end-start)+"ms");//50727ms
System.out.println(a.getI());
}
锁的状态 | 偏向锁(B) | 轻量锁(L) | 重量锁(W) | 性能比较 |
耗时 | 4424ms | 36742ms | 50727ms | B>L>W |
四:其他
4.1 对象计算过哈希后无法重新偏向
//关闭偏向锁延迟加载 -XX:BiasedLockingStartupDelay=0
public class HashLockTest {
static A a = new A();
public static void main(String[] args) {
System.out.println("before hash");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//可偏向
a.hashCode();//计算hashcode
synchronized (a){
System.out.println("after hash");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
}
System.out.println(ClassLayout.parseInstance(a).toPrintable());//无锁
}
}
4.2 调用wait方法--->重量锁
public class WaitLockTest {
static A a = new A();
public static void main(String[] args) throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (a){
System.out.println("before wait");
System.out.println(ClassLayout.parseInstance(a).toPrintable());//轻量锁
try {
a.wait();
} catch (InterruptedException e) {
}
System.out.println("after wait");
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
}).start();
Thread.sleep(7000);
synchronized (a){
a.notify();
}
}
}
执行结果:
before wait
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 90 f6 04 0b (10010000 11110110 00000100 00001011) (184874640)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 ed 27 (01000011 11100001 11101101 00100111) (669901123)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes totalafter wait
com.yew.A object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 9a 95 86 08 (10011010 10010101 10000110 00001000) (143037850)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 e1 ed 27 (01000011 11100001 11101101 00100111) (669901123)
12 4 int A.i 0
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes tota