1.缓存模型

CPU和主内存之间有好几层缓存,因为与cpu的速度相比,访问主内存的速度是非常慢的。如果频繁对同一个数据做运算,每次都从内存中加载,运算完之后再写回到主内存中,将会严重拖累cpu的计算资源。因此,为了充分发挥CPU的计算性能和吞吐量,平衡CPU和主内存之间的速度差距,现代CPU引入了一级缓存、二级缓存和三级缓存,结构如下图所示:

越靠近CPU的缓存存储速度越快,但是容量也越小。所以一级缓存(L1 Cache)最小最快,并且紧靠着在使用它的CPU内核。L2大一些,也慢一些,并且仍然只能被一个单独的 CPU 核使用。L3在现代多核机器中更普遍,仍然更大,更慢,并且被单个插槽上的所有 CPU 核共享。最后,你拥有一块主存,由全部插槽上的所有 CPU 核共享。

当CPU运算过程中需要加载数据时,首先去L1去寻找需要的数据,如果没有则去L2寻找,接着从L3中寻找,如果都没有,则从内存中读取数据。所以,如果某些数据需要经常被访问,那么这些数据存放在L1中的效率会最高。

而一般缓存未命中消耗的时钟周期及时间数据如下:

2.缓存行Cache Line

缓存是由缓存行组成的。一个缓存行存储字节的数量为2的倍数,在不同的机器上,缓存行大小为32字节到256字节不等,目前通常为64字节。

缓存行是按块加载到缓存中,一次性从内存中加载或者写入64字节的内容。假如一个Java对象有8个long类型的成员变量,那么一个缓存行最多可以一次性加载这8个long类型的变量到内存中。那么cpu在运算中,如果需要这8个变量,就可以直接从缓存中读取数据,而不需要从主内存中加载。

3.缓存一致性协议

由于一级缓存和二级缓存是被CPU核单独拥有的,当CPU核修改缓存中的内容时,缓存需要写回主内存,然后通知其他CPU核中对该缓存块进行失效处理。例如Core1和Core2并发读写同一个对象,该对象所属的内存地址块,会被同时缓存到一级缓存中。当Core1修改这个对象的变量时,改动会写回主内存,并且会让Core2缓存的该对象的缓存行失效。

4.伪共享(False Sharing)

伪共享,翻译为错误的共享。下面的图说明了伪共享的问题:

1.假设Core1更新了X值,那么缓存中的缓存行和内存中的值都会被改变,并且Core2的缓存行也都会失效,因为它缓存的X不是最新值了。那么Core2仅仅只是想读X值,却需要重新从主内存去加载,从而被缓存未命中拖慢了读取速度。

2.假设在核心1上运行的线程想更新变量X,同时核心2上的线程想要更新变量Y, 而这两个变量在同一个缓存行中。每个线程都要去竞争缓存行的所有权来更新变量。如果Core1获得了所有权,改变了X值,那么Core2中对应的缓存行失效。而Core2去读取Y时,发现缓存未命中,需要重新读取缓存行。然后当Core2去更新Y值时,也会使Core1的缓存行失效,从而使Core1引发一次缓存未命中。这会来来回回的经过L3缓存,大大影响了性能。如果互相竞争的核心位于不同的插槽,就要额外横跨插槽连接,问题可能更加严重。

5.伪共享解决办法

1.缓存行填充

disrupter框架通过增加字段,补全缓存行来确保ring buffer的序列号不会和其他东西同时存在于一个缓存行中,从而解决伪共享的问题:

 private long p1, p2, p3, p4, p5, p6, p7;private final long indexMask = 0;private long p8, p9, p10, p11, p12, p13, p14;

2.@sun.misc.Contended注解

在Java 8中,提供了@sun.misc.Contended注解来避免伪共享,原理是在使用此注解的对象或字段的前后各增加128字节大小的padding,使用2倍于大多数硬件缓存行的大小来避免相邻扇区预取导致的伪共享冲突。

    /** 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;

例如,Thread中有三个变量threadLocalRandomSeed,threadLocalRandomProbe,threadLocalRandomSecondarySeed就是通过注解的方法来操作缓存行的。不同的是,这里是让三个变量处于同一个缓存行,从而达到任意使一个变量改变,其他两个值也必须重新加载的目的。

6总结

1.缓存时为了平衡CPU和内存之间的速度差异,cpu核独自拥有L1、L2缓存,公用L3缓存,且越靠近CPU的缓存存储速度越快、容量越小。

2.缓存中的内容是按块一次性从主内存中加载到缓存行。

3.L1、L2缓存中的缓存行修改时,会写回到L3和主内存,并且使其他核上的还缓存行失效。

4.多个CPU核一写一读,或者同时写同一块缓存行的时候,会导致伪共享问题。

5.假如存在多个线程同时修改一个类的不同字段的场景,必须考虑使用缓存行填充或者@sun.misc.Contended注解防止伪共享。

参考文章:

Java8使用@sun.misc.Contended避免伪共享

剖析Disruptor:为什么会这么快?(二)神奇的缓存行填充

缓存行填充与@sun.misc.Contended注解相关推荐

  1. Random(二)什么是伪共享?@sun.misc.Contended注解

    目录 1.背景简介 2.伪共享问题 3.问题解决 4.JDK使用示例 1.背景简介 我们知道,CPU 是不能直接访问内存的,数据都是从高速缓存中加载到寄存器的,高速缓存又有 L1,L2,L3 等层级. ...

  2. Java8的@sun.misc.Contended注解

    @sun.misc.Contended 介绍 @sun.misc.Contended 是 Java 8 新增的一个注解,对某字段加上该注解则表示该字段会单独占用一个缓存行(Cache Line). 这 ...

  3. Java8的@sun.misc.Contended注解解决伪共享问题

    本文源自转载:Java8的@sun.misc.Contended注解 目录 一.@sun.misc.Contended 介绍 二.单独使用一个缓存行有什么作用--避免伪共享 三.@sun.misc.C ...

  4. IntelliJ IDEA中使用Java8的@sun.misc.Contended注解避免伪内存共享

    作用:该注解是用来消除伪内存共享的. 前提:如果要使用Contended注解,要在JVM中添加-XX:-RestrictContended参数. 遇到问题的环境:jdk12 通过如下三种方法修改JVM ...

  5. Java8的伪共享和缓存行填充--@Contended注释

    在我的前一篇文章<伪共享和缓存行填充,从Java 6, Java 7 到Java 8>中, 我们演示了在Java 8中,可以采用@Contended在类级别上的注释,来进行缓存行填充.这样 ...

  6. 聊聊java8中的@sun.misc.Contended与伪共享

    "持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第30天,点击查看活动详情" @[toc] 在前面学习ConcurrentHashMap的size方法的过程中 ...

  7. @sun.misc.Contended避免伪共享(false sharing)

    转载自:http://www.myexception.cn/program/1630142.html Java8中使用sun.misc.Contended注解来避免伪共享(false sharing) ...

  8. 什么是伪共享?Java8如何使用@sun.misc.Contended避免伪共享?

    什么是伪共享 缓存系统中是以缓存行(cache line)为单位存储的.缓存行是2的整数幂个连续字节,一般为32-256个字节.最常见的缓存行大小是64个字节.当多线程修改互相独立的变量时,如果这些变 ...

  9. Java8中用sun.misc.Contended避免伪共享(false sharing)

    转自:http://budairenqin.iteye.com/blog/2048257 关于伪共享这个概念,请先参照http://ifeve.com/falsesharing/ 伪共享的样子: Ja ...

  10. 一篇对伪共享、缓存行填充和CPU缓存讲的很透彻的文章

    认识CPU Cache CPU Cache概述 随着CPU的频率不断提升,而内存的访问速度却没有质的突破,为了弥补访问内存的速度慢,充分发挥CPU的计算资源,提高CPU整体吞吐量,在CPU与内存之间引 ...

最新文章

  1. 100例shell脚本之八远程管理获得hosts ip以及推送公钥到hosts
  2. 亿级流量架构之服务限流思路与方法
  3. 反对人工智能的九条意见是什么?
  4. 7分钟了解科大讯飞开发者节:AI红利期来临,全新1024计划发布(未完待续)
  5. 验证码的产生 python
  6. C++开发人脸性别识别教程(7)——搭建MFC框架之界面绘制
  7. Eclipse保存验证JS缓慢
  8. JBoss Fuse 6.2发布–指导如何快速尝试
  9. 华为云微服务应用平台服务能力业界领先,通过微服务标准首批评估
  10. layui prompt弹窗验证码操作
  11. tomcat Note: further occurrences of HTTP header parsing errors will be logged at DEBUG
  12. 交通肇事逃逸会受到什么处罚
  13. 如何在C ++ 中分割PDF档案?试试Aspose
  14. 找工作就上智联,效果真快,然而让我去的公司都是泡我呢
  15. 微信卡券怎么制作以及卡券封号推送消息技术分享
  16. 计算机网络自顶向下--网络层
  17. Hdu 2430 Beans (数据结构_单调队列)
  18. Python 基礎 - 文件操作_v2
  19. SwiftyJSON库的使用和思考
  20. 虚拟机通过jumpserver登录服务器,搭建 JumpServer 堡垒机管理数万台游戏服务器

热门文章

  1. 2018东北四省赛 Spell Boost DP
  2. 计算机dvd驱动错误,修正:一个错误发生在弹出的CD/DVD驱动器在Windows 10
  3. 会议OA项目(送审后审批人签字功能)
  4. 威廉 哈特 史密斯《当你抚触》
  5. Python3之入门小程序
  6. MacOS破解WiFi(WPA、WPA2)
  7. August 2007
  8. Linux阅码场原创精华文章汇总
  9. 以太坊白皮书(中文版)
  10. 基于STM32的智能抽油烟机系统