Java 为什么要在类jsr166e.Striped64.Cell中额外填充值字段?
在JSR166中引入的一个类中,作者使用所谓的填充来填充Striped64.Cell类的单值字段 以下是该课程的摘录:Java 为什么要在类jsr166e.Striped64.Cell中额外填充值字段?,java,concurrency,jvm,Java,Concurrency,Jvm,在JSR166中引入的一个类中,作者使用所谓的填充来填充Striped64.Cell类的单值字段 以下是该课程的摘录: /** * Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed * between pads, hoping that the JVM doesn't reorder them. * <p/> * JVM intrinsic
/**
* Padded variant of AtomicLong supporting only raw accesses plus CAS. The value field is placed
* between pads, hoping that the JVM doesn't reorder them.
* <p/>
* JVM intrinsics note: It would be possible to use a release-only form of CAS here, if it were
* provided.
*/
static final class Cell {
volatile long p0, p1, p2, p3, p4, p5, p6;
volatile long value;
volatile long q0, q1, q2, q3, q4, q5, q6;
...
/**
*AtomicLong的填充变体,仅支持原始访问和CA。此时将放置“值”字段
*在pad之间,希望JVM不会对它们重新排序。
*
*JVM内部注意:如果是,这里可以使用CA的一种只发布的形式
*提供。
*/
静态最终类单元{
挥发性长p0、p1、p2、p3、p4、p5、p6;
波动性长值;
波动性长q0、q1、q2、q3、q4、q5、q6;
...
然后,作者使用CAS以原子方式修改该值
在Striped64类中,作者还使用Unsafe访问其他两个字段,但不应用任何此类填充
我的问题是:为什么要做这样的事情,引入14个冗余字段来填充单个值字段?填充是为了防止共享
值的缓存线,否则可能需要从内存中重新获取值,因为缓存线上的其他内容需要整个生产线都将失效。因此,目标是提高性能
为了让事情变得更简单,Java8引入了,它在幕后做同样的事情,只是它是由JVM自己处理的。虽然我同意assylias的回答,但我认为它需要一些解释
为什么缓存未命中很重要?
因为从主存中读取要比从缓存中读取慢得多。如果您有一个需要经常使用的变量,则将其放入缓存非常重要。此外,如果此变量与其他变量共享同一缓存,则整个缓存线可能会失效
考虑variable1与variable2位于同一缓存上的示例。variable1由thread1使用,variable2由thread2使用。由于它们位于同一缓存线上,因此如果有variable2的更新,thread1需要使用variable1,则需要删除缓存线(即使它不使用此变量!)并从主存读取。这称为错误共享
为什么实际上还有7个多头?
如果仅当JVM不决定对内存重新排序时,则从何处开始读取该变量并不重要(可以从8个“缓存行”中的第3行读取)-缓存线中仍会有一个值。因此,无论您从何处开始读取,缓存线中只会有一个对您重要的值,因此不可能出现“错误共享”的“缓存未命中”
另外,这就是为什么Java对象的大小可以被8整除。谢谢你的回答。我认为这是正确的,但为什么作为p0..p6+值的总大小的额外q0…q6已经是64字节了,据我所知,这将完全适合单个缓存线?是为了适应实现更长缓存线的其他体系结构吗ines?@SilkEntry它将使用2条缓存线。如果您只有p0,p1…,p6,value,那么您很可能会在一条缓存线上有p0-p6,以及其他一些数据,在另一条缓存线上有值,以及其他数据。