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

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值