Java中的僞共享(false sharing)

Java中的僞共享(false sharing)

1.引入
最近在看JVM最新的一版,挺不錯的比之前的第二版增加介紹了jdk8之後JVM的改變等。(推薦大家去看!!!)然後在卡表哪裏提到了僞共享,於是就有了下面的文章了。(可能有很多大佬都寫過了,我在來寫一遍,加強我自己的理解吧。)

2.什麼是僞共享?
維基百科對於僞共享的解釋:CPU的緩存是以緩存行(cache line)爲單位進行緩存的,當多個線程修改不同變量,而這些變量又處於同一個緩存行時就會影響彼此的性能。例如:線程1和線程2共享一個緩存行,線程1只讀取緩存行中的變量1,線程2修改緩存行中的變量2,雖然線程1和線程2操作的是不同的變量,由於變量1和變量2同處於一個緩存行中,當變量2被修改後,緩存行失效,線程1要重新從主存中讀取,因此導致緩存失效,從而產生性能問題。爲了更深入一步理解僞共享,我們先看一下CPU緩存。
總體來說就是線程同時訪問緩存行導致了,數據在緩存行裏的緩存數據失效。

3.java中的僞共享問題

private static final int DIMENSION_1 = 1024 * 1024;
    private static final int DIMENSION_2 = 62;

    private static long[][] longs;

    public static void main(String[] args) throws Exception {
        longs = new long[DIMENSION_1][];
        for (int i = 0; i < DIMENSION_1; i++) {
            longs[i] = new long[DIMENSION_2];
            for (int j = 0; j < DIMENSION_2; j++) {
                longs[i][j] = 0L;
            }
        }
        System.out.println("starting....");

        final long start = System.currentTimeMillis();
        long sum = 0L;
        for (int r = 0; r < 10; r++) {

            // 1----------------------------
            for (int j = 0; j < DIMENSION_2; j++) {
                for (int i = 0; i < DIMENSION_1; i++) {
                    sum += longs[i][j];
                }
            }
            // 2----------------------------

            // 3----------------------------
//            for (int i = 0; i < DIMENSION_1; i++) {
//                for (int j = 0; j < DIMENSION_2; j++) {
//                    sum += longs[i][j];
//                }
//            }
            // 4----------------------------
        }
        System.out.println("執行時間 = " + (System.currentTimeMillis() - start));
    }

3-4行快得到原因:

3-4行運算,每次開始內循環時,從內存抓取的數據塊實際上覆蓋了longs[i][0]到longs[i][5]的全部數據(剛好64字節)。
因此,內循環時所有的數據都在L1緩存可以命中,遍歷將非常快。

1-2行慢的原因:

1-2行運算,每次從內存抓取的都是同行不同列的數據塊(如longs[i][0]到longs[i][5]的全部數據),但循環下一個的目標,
卻是同列不同行(如longs[0][0]下一個是longs[1][0],造成了longs[0][1]-longs[0][5]無法重複利用

4.java中的僞共享問題
Java 8 中已經提供了官方的解決方案,Java 8 中新增了一個註解: @sun.misc.Contended。加上這個註解的類會自動補齊緩存行,需要注意的是此註解默認是無效的,需要在 jvm 啓動時設置 -XX:-RestrictContended 纔會生效。
同時ConcurrentHashMap 中也用到了這個@sun.misc.Contended 註解來解決僞共享。

/**
     * A padded cell for distributing counts.  Adapted from LongAdder
     * and Striped64.  See their internal docs for explanation.
     */
    @sun.misc.Contended static final class CounterCell {
        volatile long value;
        CounterCell(long x) { value = x; }
    }
-XX:+UseCondCardMark,用來決定是否開啓 卡表更新的條件判斷。這個是解決內存回收時的卡表判斷數據是否是需要回收的數據,來解決僞共享問題。
	但是實際開發中一般很難遇到。如果真的遇到了,那將是一個奇怪知識爆增的時機。

引入文章:https://blog.csdn.net/xiamiflying/article/details/80910680

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章