synchronized批量重偏向與批量撤銷
- 批量重偏向:如果一個類的大量物件被一個執行緒T1執行了同步操作,也就是大量物件先偏向了T1,T1同步結束后,另一個執行緒也將這些物件作為鎖物件進行操作,會導偏向鎖重偏向的操作,
- 批量撤銷:當一個偏向鎖如果撤銷次數到達40的時候就認為這個物件設計的有問題;那么JVM會把這個物件所對應的類所有的物件都撤銷偏向鎖;并且新實體化的物件也是不可偏向的,
可以通過命令java -XX:+PrintFlagsFinal -version|grep 'BiasedLocking'來看JVM中的默認配置:
intx BiasedLockingBulkRebiasThreshold = 20 {product}
intx BiasedLockingBulkRevokeThreshold = 40 {product}
intx BiasedLockingDecayTime = 25000 {product}
intx BiasedLockingStartupDelay = 4000 {product}
bool TraceBiasedLocking = false {product}
bool UseBiasedLocking = true {product}
- BiasedLockingBulkRebiasThreshold:偏向鎖批量重偏向的默認閥值為20次,
- BiasedLockingBulkRevokeThreshold:偏向鎖批量撤銷的默認閥值為40次,
- BiasedLockingDecayTime:距上次批量重偏向25秒內,撤銷計數達到40,就會發生批量撤銷,每隔(>=)25秒,會重置在[20, 40)內的計數,這意味著可以發生多次批量重偏向,
批量重偏向(BulkRebias)
測驗代碼:
package com.morris.concurrent.syn.batch;
import org.openjdk.jol.info.ClassLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class BulkBias {
private static Thread t1, t2;
public static void main(String[] args) throws InterruptedException {
// 延時產生可偏向物件
TimeUnit.SECONDS.sleep(5);
List<B> objects = new ArrayList<>(); // 創建50個物件,鎖狀態為101,匿名偏向鎖
for (int i = 0; i < 50; i++) {
objects.add(new B());
}
t1 = new Thread(() -> {
for (int i = 0; i < objects.size(); i++) {
synchronized (objects.get(i)) { // 50個物件全部偏向t1 101
}
}
LockSupport.unpark(t2);
});
t2 = new Thread(() -> {
LockSupport.park();
//這里面只回圈了30次!!!
for (int i = 0; i < 30; i++) {
Object a = objects.get(i);
synchronized (a) {
//分別列印第19次和第20次偏向鎖重偏向結果
if (i == 18 || i == 19) {
System.out.println("第" + (i + 1) + "次偏向結果");
System.out.println((ClassLayout.parseInstance(a).toPrintable())); // 第19次輕量級鎖00,第20次偏向鎖101,偏向t2
}
}
}
});
t1.start();
t2.start();
t2.join();
System.out.println("列印list中第11個物件的物件頭:");
System.out.println((ClassLayout.parseInstance(objects.get(10)).toPrintable())); // 01 無鎖
System.out.println("列印list中第26個物件的物件頭:");
System.out.println((ClassLayout.parseInstance(objects.get(25)).toPrintable())); // 101 偏向t2
System.out.println("列印list中第41個物件的物件頭:");
System.out.println((ClassLayout.parseInstance(objects.get(40)).toPrintable())); // 101 偏向t1
}
}
class B {
}
運行結果如下:
第19次偏向結果
com.morris.concurrent.syn.batch.B object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 40 f3 bc 1d (01000000 11110011 10111100 00011101) (498922304)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
第20次偏向結果
com.morris.concurrent.syn.batch.B object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 69 6c 1e (00000101 01101001 01101100 00011110) (510421253)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
列印list中第11個物件的物件頭:
com.morris.concurrent.syn.batch.B 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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
列印list中第26個物件的物件頭:
com.morris.concurrent.syn.batch.B object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 69 6c 1e (00000101 01101001 01101100 00011110) (510421253)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
列印list中第41個物件的物件頭:
com.morris.concurrent.syn.batch.B object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 60 6c 1e (00000101 01100000 01101100 00011110) (510418949)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
運行結果分析:
- 當一個執行緒t1運行結束后,所有的物件都偏向t1,
- 執行緒t2只對前30個物件進行了同步,0-18的物件會偏向鎖(101)升級為輕量級鎖(00),19-29的物件由于撤銷次數達到20,觸發批量重偏向,偏向執行緒t2,
- t2結束后,0-18的物件由輕量級鎖釋放后變成了無鎖,19-29的物件偏向t2,30-49的物件還是偏向t1,
總結:批量重偏向會以class為單位,為每個class維護一個偏向鎖撤銷計數器,每一次該class的物件發生偏向撤銷操作時,該計數器+1,當這個值達到重偏向閾值(默認20)時,JVM就認為該class的偏向鎖有問題,因此會進行批量重偏向,
批量撤銷(BulkRevoke)
測驗代碼如下:
package com.morris.concurrent.syn.batch;
import org.openjdk.jol.info.ClassLayout;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
public class BulkBiasAndRevoke {
private static Thread t1, t2, t3, t4;
public static void main(String[] args) throws InterruptedException {
TimeUnit.SECONDS.sleep(5); // 等待偏向延遲時間到達
List<L> list = new ArrayList<>();
for (int i = 0; i < 80; i++) {
list.add(new L());
}
t1 = new Thread(() -> {
for (int i = 0; i < 60; i++) {
L l = list.get(i);
synchronized (l) {
}
}
LockSupport.unpark(t2);
}, "t1");
t2 = new Thread(() -> {
LockSupport.park();
for (int i = 0; i < 60; i++) {
L l = list.get(i);
synchronized (l) {
}
}
}, "t2");
t3 = new Thread(() -> {
LockSupport.park();
System.out.println("t3");
for (int i = 0; i < 60; i++) {
L l = list.get(i);
// 0-18 01
// 19-59 101 偏向t2
synchronized (l) {
// 0-59 00
}
// 0-59 01
}
}, "t3");
t4 = new Thread(() -> {
synchronized (list.get(65)) {
System.out.println("t4 begin" + ClassLayout.parseInstance(list.get(65)).toPrintable()); // 101
LockSupport.unpark(t3);
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t4 end" + ClassLayout.parseInstance(list.get(65)).toPrintable()); // 00
System.out.println("t4 end" + ClassLayout.parseInstance(list.get(66)).toPrintable()); // 101
}
}, "t1");
t4.start();
t1.start();
t2.start();
t3.start();
t3.join();
t4.join();
System.out.println(ClassLayout.parseInstance(new L()).toPrintable()); // 01
}
}
class L {
}
運行結果如下:
t4 begincom.morris.concurrent.syn.batch.L object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 61 79 1e (00000101 01100001 01111001 00011110) (511271173)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
t4 endcom.morris.concurrent.syn.batch.L object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 28 ef c2 1d (00101000 11101111 11000010 00011101) (499314472)
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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
t4 endcom.morris.concurrent.syn.batch.L 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 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
com.morris.concurrent.syn.batch.L 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 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
- t1執行完后,0-59的物件偏向t1,
- t2執行完后,0-18的物件為無鎖,19-59偏向t2,
- t3執行時由于之前執行過批量重偏向了,所以這里會升級為輕量級鎖,
- t4休眠前物件65為匿名偏向狀態,t4休眠后,由于觸發了批量撤銷,所以鎖狀態變為輕量級鎖,所以批量撤銷會把正在執行同步的物件的鎖狀態由偏向鎖變為輕量級鎖,而不在執行同步的物件的鎖狀態不會改變(如物件66),
總結:批量重偏向和批量撤銷是針對類的優化,和物件無關,偏向鎖重偏向一次之后不可再次重偏向,當某個類已經觸發批量撤銷機制后,JVM會默認當前類產生了嚴重的問題,剝奪了該類的新實體物件使用偏向鎖的權利,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/33122.html
標籤:其他
上一篇:手機硬改
下一篇:r9s 刷回官方re
