一段代码使用@sun.misc.Contended注解后竟然…
竟然变快了。
先放代码。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| import java.util.concurrent.CountDownLatch;
import org.openjdk.jol.info.ClassLayout;
public class ContendedTest {
private static final long N = 100000000L;
private static class T {
// private long x1,x2,x3,x4,x5,x6,x7;
@sun.misc.Contended
private volatile long x = 0L;
// private long x8,x9,x10,x11,x12,x13,x14;
}
private static T[] ARR = new T[2];
static {
ARR[0] = new T();
ARR[1] = new T();
}
public static void main(String[] args) throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(2);
long start = System.currentTimeMillis();
new Thread(() -> {
for(int i = 0; i < N; i++) {
ARR[0].x = 1L;
}
cdl.countDown();
}).start();
new Thread(() ->{
for(int i = 0; i < N; i++) {
ARR[1].x = 1L;
}
cdl.countDown();
}).start();
cdl.await();
System.out.println(System.currentTimeMillis() - start);
System.out.println(ClassLayout.parseInstance(new T()).toPrintable());
}
}
|
加与不加@Contended注解耗时相差1s
(使用注解需要加上jvm参数**-XX:-RestrictContended**)。
为什么耗时会相差这么多呢?
这就涉及到两个概念缓存行和伪共享。
根据局部性原理我们知道CPU会取一块连续区域的数据。而缓存呢又以缓存行为单位,所以当两个CPU使用了同一个缓存行的数据就会导致数据不一致性问题,需要耗时来达到缓存一致性。
比如上面例子的ARR[0]和ARR[1],在同一个缓存行的时候被两个CPU修改就需要耗时来达到缓存一致。
由于通常情况下,一个缓存行为64字节,而一个long变量为8字节,所以我们可以在x变量的前后加上7个long变量来使ARR[0]和ARR[1]的x不在同一缓存行,这样就不会有缓存不一致的问题。高性能队列Disruptor里就有这种写法。
到了Java8,有了@sun.misc.Contended注解,就可以不用变量填充来解决伪共享的问题了。
当然,想要时间得要空间来换,使用JOL可以看到不同情况下对象T的大小。
无处理
1
2
3
4
5
6
7
8
| OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c0 00 20 (01000011 11000000 00000000 00100000) (536920131)
12 4 (alignment/padding gap)
16 8 long T.x 0
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
|
变量填充
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c0 00 20 (01000011 11000000 00000000 00100000) (536920131)
12 4 (alignment/padding gap)
16 8 long T.x1 0
24 8 long T.x2 0
32 8 long T.x3 0
40 8 long T.x4 0
48 8 long T.x5 0
56 8 long T.x6 0
64 8 long T.x7 0
72 8 long T.x 0
80 8 long T.x8 0
88 8 long T.x9 0
96 8 long T.x10 0
104 8 long T.x11 0
112 8 long T.x12 0
120 8 long T.x13 0
128 8 long T.x14 0
Instance size: 136 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
|
注解
1
2
3
4
5
6
7
8
9
| OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c0 00 20 (01000011 11000000 00000000 00100000) (536920131)
12 132 (alignment/padding gap)
144 8 long T.x 0
152 0 (loss due to the next object alignment)
Instance size: 280 bytes
Space losses: 132 bytes internal + 0 bytes external = 132 bytes total
|