赞
踩
为了解决计算机系统中主内存与 CPU 之间运行速度差问题,会在 CPU 与主内存之间 添加一级或者多级高速缓冲存储器( Cache)。这个 Cache 一般是被集成到 CPU 内部的, 所以也叫 CPU Cache,如图所示是两级 Cache 结构。
在 Cache 内部是按行存储的,其中每一行称为一个 Cache行。 Cache行 是 Cache 与主内存进行数据交换的单位,
Cache 行的大小一般为 2 的幕次数字节。
目前主流的CPU Cache的Cache Line大小都是64Bytes。假设我们有一个512字节的一级缓存,那么按照64B的缓存单位大小来算,这个一级缓存所能存放的缓存个数就是512/64 = 8
个。具体参见下图:
Cache Line可以简单的理解为CPU Cache中的最小缓存单位。
- [root@bj-rack001 index0]#
- [root@bj-rack001 index0]# pwd
- /sys/devices/system/cpu/cpu1/cache/index0
- [root@bj-rack001 index0]#
- [root@bj-rack001 index0]#
- [root@bj-rack001 index0]#
- [root@bj-rack001 index0]# more /sys/devices/system/cpu/cpu1/cache/index0/coherency_line_size
- 64
- [root@bj-rack001 index0]#
- [root@bj-rack001 index0]#
CPU 访问某个变 量 时,首先会去看 CPU Cache 内是否有该变量,如果有则直接从 中获取,否则就去主内存里面获取该变 量 ,然后把该变量所在内存区域的一个 Cache 行大 小的内存复制到 Cache 中。 由于存放到 Cache行的是内存块而不是单个变量,所以可能会把多个变量存放到一个 Cache行中。 当多个线程同时修改一个缓存行里面的多个变量时, 由于同时只能有 一 个线程操作 缓存行 ,所以相比将 每个变 量放到 一 个缓存行,性能会有所 下降,这就是伪共享,如图 2-8 所示 。
伪共享的产生是因为 多个变量被放入了一个缓存行中,并且多个线程 同时去写入缓存 行中不同的变量 。
那么 为何 多个变量会被放入 一个缓存行呢?
其实是 因为缓存与内存交换 数据 的单位就是缓存行 , 当 CPU 要访问的变量没有在缓存中找到时,根据程序运行的局部性原理, 会把该变量所在内存中大小为缓存行的内存放入缓存行。
也就是地址连续的多个变量才有可能会被放到一个缓存行中。当创建数组时, 数组里面的多个元素就会被放入同一 个 缓存 行 。
实在正常情况下单线程访 问时将数组元素放入一个或者多个缓存 行对代码执行是有利的,因为数据都在缓存 中 ,代码执行会更快
在 JDK 8 之前 一般都是通过字节填充的方 式来避免该问题,也就是创建 一个变量时使 用填充字段填充该变量所在的缓存行,这样就避免了将多个变量存放在同 一个缓存行中,
JDK 8提供了一个 sun.misc.Contended注解,用来解决伪共享问题。将上面代码修改 为如下。
- /** The current seed for a ThreadLocalRandom */
- @sun.misc.Contended("tlr")
- long threadLocalRandomSeed;
-
- /** Probe hash value; nonzero if threadLocalRandomSeed initialized */
- @sun.misc.Contended("tlr")
- int threadLocalRandomProbe;
-
- /** Secondary seed isolated from public ThreadLocalRandom sequence */
- @sun.misc.Contended("tlr")
- int threadLocalRandomSecondarySeed;
@Contended 注解只用于 Java 核心 类, 比如此包下的 类。
如果用户类路径下的类需要使用这个注解, 则 需要添加 NM 参数 :-XX:-RestrictContended。
填充的宽度默认为 128,要自定义宽度 则可以设 置 -XX:Con nd巳dPaddingWidth 参数。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。