伪共享(False Sharing)和缓存行(Cache Line)

本文深入探讨了多核处理器环境中缓存行的工作原理及其对性能的影响,详细讲解了伪共享现象及其解决方案,包括通过填充变量和使用@Contended注解来避免伪共享。

缓存行(Cache Line)

直接说重点,概念什么的请自行百度,用最通俗的话来讲就是多核计算机的一个处理器会有多个核,每个核中会存在L1、L2缓存,多个核之间共享L3缓存,画个简单的图来表示一下:
图片1-1
变量位置与访问效率对比:

位置 执行效率
寄存器中 1个周期
CPU CACHE中 1~30个周期
主存中 50~200个周期
磁盘中 几千万个周期

以常见的windows操作系统来说,一个缓存行一般是64个字节的大小,cpu每次访问数据时,会逐一从L1,L2,L3中寻找,如果没有则会去主存中读取,读取不到会去磁盘中寻找,使用缓存需要发挥局部性原理,时间局部性空间局部性

时间局部性

例如有一个变量会被多次访问,那么把它加载到缓存中,后面多次访问都直接从缓存中命中。

空间局部性

可以充分利用64字节的大小,一次性把后续程序要用到的数据都加载进来,例如一个数组,可以把其全部加载进来,后面使用时会直接从缓存命中。

伪共享(False Sharing)

对象的成员变量在多线程环境下使用时,会出现一个伪共享False Sharing的概念。
先来复习一下基础知识
1.Java对象头都占8个字节
2.成员变量占用字节大小如下表

顺序 类型 字节数量
1 double 8字节
2 long 8字节
3 int 4字节
4 float 4字节
5 short 2字节
6 char 2字节
7 boolean 1字节
8 byte 1字节
9 对象引用 4或者8字节
10 子类字段 重新排序

那么在Java代码中如何产生伪共享呢?直接在代码中解释

public class FalseSharing {
   
   
    private int x;
    private int y;
    private int z;
}

上述代码中x,y,z由于缓存行的局部性原理,会一次性加载到统一缓存行中
在这里插入图片描述
根据MESI协议,当线程A尝试修改x变量时,同时线程B尝试修改z变量,看似两个互不相干的线程操作,实际上已经产生了伪共享,线程A在core1上优先修改后,这时这个缓存行状态已经由共享状态变更为修改状态,并且发起写操作至主存,并且会通知其他cpu核此时这个缓存行已经无效,需要去主存中重新读取缓存行,之后再进行线程B的写操作,看似两个毫无相关的变量已经在多线程环境下产生了先后顺序。

伪共享的解决方案

JDK6的时代,可以通过增加一些无用的long类型变量,并且加上对象头的8个字节,刚好把内存扩展到64字节或者64的整数倍。

 public final static class VolatileLong {
   
    
        public volatile 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

代码大师麦克劳瑞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值