Java8的偽共用和緩衝行填充--@Contended注釋,java8--@contended

來源:互聯網
上載者:User

Java8的偽共用和緩衝行填充--@Contended注釋,java8--@contended

在我的前一篇文章<偽共用和緩衝行填充,從Java 6, Java 7 到Java 8>中, 我們示範了在Java 8中,可以採用@Contended在類層級上的注釋,來進行緩衝行填充。這樣,多線程情況下的偽共用衝突問題。 感興趣的同學可以查看該文。

 

其實,@Contended注釋還可以應用於欄位層級(Field-Level),當應用於欄位層級時,被注釋的欄位將和其他欄位隔離開來,會被載入在獨立的緩衝行上。在欄位層級上,@Contended還支援一個“contention group”屬性(Class-Level不支援),同一group的欄位們在記憶體上將是連續,但和其他他欄位隔離開來。

 

上面只是泛泛的介紹一下。關於@Contended應用於Field-Level特別是contention group的相關的資料很少,原始碼中的注釋中有一些,還有關於JEP-142(即關於增加@Contended的提議)的郵件討論群組中的描述(http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html),其中的講解是非常詳細的(由於該討論發生在@Contended實現的最初階段,不能保證和現在的實現完全一致), 我摘錄和翻譯如下:

 

@Contended注釋的行為如下所示:

A,在類上應用Contended:

@Contended    public static class ContendedTest2 {        private Object plainField1;        private Object plainField2;        private Object plainField3;        private Object plainField4;    }

 

將使整個欄位塊的兩端都被填充:(以下是使用 –XX:+PrintFieldLayout的輸出)(翻譯註:注意前面的@140表示欄位在類中的地址位移)

TestContended$ContendedTest2: field layout    Entire class is marked contended     @140 --- instance fields start ---     @140 "plainField1" Ljava.lang.Object;     @144 "plainField2" Ljava.lang.Object;     @148 "plainField3" Ljava.lang.Object;     @152 "plainField4" Ljava.lang.Object;     @288 --- instance fields end ---     @288 --- instance ends ---

 

注意,我們使用了128bytes的填充 -- 2倍於大多數硬體緩衝行的大小 -- 來避免相鄰扇區預取導致的偽共用衝突。

 

B,在欄位上應用Contended:

public static class ContendedTest1 {        @Contended        private Object contendedField1;        private Object plainField1;        private Object plainField2;        private Object plainField3;        private Object plainField4;    }

 

將導致該欄位從連續的欄位塊中分離開來並高效的添加填充:

TestContended$ContendedTest1: field layout     @ 12 --- instance fields start ---     @ 12 "plainField1" Ljava.lang.Object;     @ 16 "plainField2" Ljava.lang.Object;     @ 20 "plainField3" Ljava.lang.Object;     @ 24 "plainField4" Ljava.lang.Object;     @156 "contendedField1" Ljava.lang.Object; (contended, group = 0)     @288 --- instance fields end ---     @288 --- instance ends ---

 

C, 註解多個欄位使他們分別被填充:

public static class ContendedTest4 {        @Contended        private Object contendedField1;        @Contended        private Object contendedField2;        private Object plainField3;        private Object plainField4;    }

 

被註解的2個欄位都被獨立地填充:

TestContended$ContendedTest4: field layout     @ 12 --- instance fields start ---     @ 12 "plainField3" Ljava.lang.Object;     @ 16 "plainField4" Ljava.lang.Object;     @148 "contendedField1" Ljava.lang.Object; (contended, group = 0)     @280 "contendedField2" Ljava.lang.Object; (contended, group = 0)     @416 --- instance fields end ---     @416 --- instance ends ---

 

在有些cases中,你會想對欄位進行分組,同一組的欄位會和其他欄位有存取違規,但是和同一組的沒有。例如,(同一個線程的)代碼同時更新2個欄位是很常見的情況。如果同時把2個欄位都添加@Contended註解是足夠的(翻譯註:但是太足夠了),但我們可以通過去掉他們之間的填充,來最佳化它們的記憶體空間佔用。為了區分組,我們有一個參數“contention group”來描述:

所以:

public static class ContendedTest5 {        @Contended("updater1")        private Object contendedField1;        @Contended("updater1")        private Object contendedField2;        @Contended("updater2")        private Object contendedField3;        private Object plainField5;        private Object plainField6;    }

 

記憶體布局是:

TestContended$ContendedTest5: field layout     @ 12 --- instance fields start ---     @ 12 "plainField5" Ljava.lang.Object;     @ 16 "plainField6" Ljava.lang.Object;     @148 "contendedField1" Ljava.lang.Object; (contended, group = 12)     @152 "contendedField2" Ljava.lang.Object; (contended, group = 12)     @284 "contendedField3" Ljava.lang.Object; (contended, group = 15)     @416 --- instance fields end ---     @416 --- instance ends ---

 

注意$contendedField1 和$contendedField2和其他欄位之間有填充,但是它們之間是緊挨著的。
 
 
以上是對郵件組中大牛們原始實現解釋的翻譯。
 
下面我們來做一個測試,看@Contended在欄位層級,並且帶分組的情況下,是否能解決偽緩衝問題。
import sun.misc.Contended;public class VolatileLong {    @Contended("group0")    public volatile long value1 = 0L;      @Contended("group0")    public volatile long value2 = 0L;          @Contended("group1")    public volatile long value3 = 0L;      @Contended("group1")    public volatile long value4 = 0L;  }

 

我們用2個線程來修改欄位--

測試1:線程0修改value1和value2;線程1修改value3和value4;他們都在同一組中。

測試2:線程0修改value1和value3;線程1修改value2和value4;他們在不同組中。

 
測試1:
public final class FalseSharing implements Runnable {    public final static long ITERATIONS = 500L * 1000L * 1000L;    private static VolatileLong volatileLong;    private String groupId;    public FalseSharing(String groupId) {        this.groupId = groupId;    }    public static void main(final String[] args) throws Exception {        // Thread.sleep(10000);        System.out.println("starting....");        volatileLong = new VolatileLong();        final long start = System.nanoTime();        runTest();        System.out.println("duration = " + (System.nanoTime() - start));    }    private static void runTest() throws InterruptedException {        Thread t0 = new Thread(new FalseSharing("t0"));        Thread t1 = new Thread(new FalseSharing("t1"));        t0.start();        t1.start();        t0.join();        t1.join();    }    public void run() {        long i = ITERATIONS + 1;        if (groupId.equals("t0")) {            while (0 != --i) {                volatileLong.value1 = i;                volatileLong.value2 = i;            }        } else if (groupId.equals("t1")) {            while (0 != --i) {                volatileLong.value3 = i;                volatileLong.value4 = i;            }        }    }}
 
測試2:(基於以上代碼修改下面的部分)
public void run() {        long i = ITERATIONS + 1;        if (groupId.equals("t0")) {            while (0 != --i) {                volatileLong.value1 = i;                volatileLong.value3 = i;            }        } else if (groupId.equals("t1")) {            while (0 != --i) {                volatileLong.value2 = i;                volatileLong.value4 = i;            }        }    }

 

測試1:

starting....duration = 16821484056

測試2:

starting....duration = 39191867777

 

可以看出,如果同一線程修改的是同一“contention group”中的欄位,沒有偽共用衝突,比有偽共用衝突的情況要快1倍多。

 

後記:

測試3:不使用@Contended

public class VolatileLong {    public volatile long value1 = 0L;      public volatile long value2 = 0L;      public volatile long value3 = 0L;      public volatile long value4 = 0L;  }

結果:

starting....duration = 38096777198

 

 

參考:

http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/sun/misc/Contended.java

http://openjdk.java.net/jeps/142

http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.