为什么java的Exchanger.Slot缓存行填充像这样?

时间:2016-02-24 15:14:42

标签: java disruptor-pattern

当我读到“错误分享”时java中的mechnism,我在 java.util.concurrent.Exchanger.Slot

中找到以下代码
    /**
 * A Slot is an AtomicReference with heuristic padding to lessen
 * cache effects of this heavily CAS'ed location.  While the
 * padding adds noticeable space, all slots are created only on
 * demand, and there will be more than one of them only when it
 * would improve throughput more than enough to outweigh using
 * extra space.
 */
private static final class Slot extends AtomicReference<Object> {
    // Improve likelihood of isolation on <= 64 byte cache lines
    long q0, q1, q2, q3, q4, q5, q6, q7, q8, q9, qa, qb, qc, qd, qe;
}

很奇怪。为什么长的数是15(q0-qe), 所以我可以计算出物体的大小应该是:
15 * 8(长)+ 8(父长值)+ 16 (64位jvm处的对象标题指针)= 144字节。
或:
15 * 8(长)+ 8(父长值)+ 8 (32位jvm处的对象标题指针)= 136字节。
当我读到Disruptor's implemention:

public long p1, p2, p3, p4, p5, p6, p7; // cache line padding

private volatile long cursor = INITIAL_CURSOR_VALUE;

public long p8, p9, p10, p11, p12, p13, p14; // cache line padding

尺寸应为7 * 8 + 8 + 7 * 8 + 8(物体头opinter&#39;尺寸为32 bit jvm)= 128 = 64 * 2.
由于大多数cpu的默认缓存行大小为64字节,因此破解者的impl可以避免“错误共享”。
所以我的问题是在课堂上填充的数量 java.util.concurrent.Exchanger.Slot 是对还是错?

2 个答案:

答案 0 :(得分:0)

java.util.concurrent.Exchanger.Slot中的填充正在安全地播放。它正在添加额外的填充,而另一方面,破坏者正在添加足够的填充。

如果JVM重新排序字段并且高速缓存行大小为128位,则破坏程序方法也可能失败。最安全的方法是通过继承来分隔字段:

class MemoryFieldAddress {
    private long address;

    public final long getAddress() {
        return address;
    }

    protected final void setAddress(final long address) {
        this.address = address;
    }
}

class MemoryAddressPad1 extends MemoryFieldAddress {
    long p1_1, p1_2, p1_3, p1_4, p1_5, p1_6, p1_7, p1_8, p1_9, p1_10, p1_11, p1_12, p1_13, p1_14, p1_15, p1_16, p1_17;
}

class MemoryFieldBytes extends MemoryAddressPad1 {
    private long bytes;

    public final long getBytes() {
        return bytes;
    }

    protected final void setBytes(final long bytes) {
        this.bytes = bytes;
    }
}

class MemoryAddressPad2 extends MemoryFieldBytes {
    long p2_1, p2_2, p2_3, p2_4, p2_5, p2_6, p2_7, p2_8, p2_9, p2_10, p2_11, p2_12, p2_13, p2_14, p2_15, p2_16, p2_17;
}

// Finally the full implimentation
public class Memory extends MemoryAddressPad2 {}

如果您愿意,可以计算最终字段,很少更新字段以填充。

答案 1 :(得分:0)

为了回答你的问题,你需要知道java obj的结构,默认情况下,java按顺序排列obj的字段,如long / double,int,oops,....例如: 你看到的如下:

public class testObj{
    int a,b,c;
    Object obj1;
    Object obj2;
    short s1,s2;
    long  l1,l2;
}

但实际上,安排如下

    public class testObj{

    long l1,l2;
    int a,b,c;
    short s1,s2
    Object 1
    Object 2
}

这意味着长字段q1 ....... qe将被安排在java obj的开头,这意味着至少它可以在你要保护的字段之前填充缓存行,其余的对象,它们可以占用高速缓存行的其余部分

  1. cacheline 1 ---------- ++++++
  2. 缓存行2 ++++++++++++++++++++++++++
  3. 缓存行3 ******
  4. (X)表示您要保护的字段

    (+)表示填充长字段的120个字节

    (*)表示obj的其余部分

    对于你的情况,它是一个数组,它是顺序的,所以它将是

      

    cacheline:xxxxxxxx array [0] xx

         

    cacheline:xxxxxxx array [1] xx

    所有这些x都是长槽填充