Java8中用sun.misc.Contended避免伪共享(false sharing)
来源:互联网 发布:南京师范大学网络 编辑:程序博客网 时间:2024/05/18 09:01
http://budairenqin.iteye.com/blog/2048257
关于伪共享这个概念,请先参照http://ifeve.com/falsesharing/
伪共享的样子:
我们看到,两个VolatileLong对象被load到了同一个缓存行里面,如果一个线程要修改对象1,另一个线程同时要修改对象2,此时就要面对伪共享这个无形的性能杀手了
jdk6中的解决办法:
很多大神的代码都是通过上面的方式也就是long padding来避免伪共享
例如:
1.Doug Lea的jsr166中早期的LinkedTransferQueue版本http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/LinkedTransferQueue.java?revision=1.1&view=markup
2.还是Doug Lea的ConcurrentHashMapV8http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.121&view=markup(在java8中的版本已经用sun.misc.Contended替换long padding)
3.大名鼎鼎的无锁并发框架Disruptor https://github.com/LMAX-Exchange/disruptor
4.等等等...
long padding的解决办法不怎么优雅,并且在jdk7某个版本以后能会优化掉long padding,尼玛java程序员是有多难啊,想尽了一切办法对付jdk7中的这个优化,详情点击http://ifeve.com/false-sharing-java-7/
但是但是...开始使用jdk8的同学注意了,java8已经给出了官方的解决办法...
就是sun.misc.Contended注解http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html
要注意的是user classpath使用此注解默认是无效的,需要在jvm启动时设置-XX:-RestrictContended
jdk8中已经使用sun.misc.Contended的地方:
最后,贴上测试代码,感兴趣的各自测试吧
我机器上的测试结果:
测试代码来自http://ifeve.com/falsesharing/,稍有改动
伪共享的样子:
- Java view:
- public class VolatileLong {
- volatile long v = 0L;
- }
- Memory view:
- ...–––-)(––––HV––––HV–––)(–––...
我们看到,两个VolatileLong对象被load到了同一个缓存行里面,如果一个线程要修改对象1,另一个线程同时要修改对象2,此时就要面对伪共享这个无形的性能杀手了
jdk6中的解决办法:
- Java view:
- public class VolatileLong {
- volatile long p0, p1, p2, p3, p4, p5, p6;
- volatile long v = 0L;
- volatile long q0, q1, q2, q3, q4, q5, q6;
- }
- Memory view:
- ...–––-)(ppppppHVqqqqqq)(–––...
很多大神的代码都是通过上面的方式也就是long padding来避免伪共享
例如:
1.Doug Lea的jsr166中早期的LinkedTransferQueue版本http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166y/LinkedTransferQueue.java?revision=1.1&view=markup
2.还是Doug Lea的ConcurrentHashMapV8http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/ConcurrentHashMapV8.java?revision=1.121&view=markup(在java8中的版本已经用sun.misc.Contended替换long padding)
3.大名鼎鼎的无锁并发框架Disruptor https://github.com/LMAX-Exchange/disruptor
4.等等等...
long padding的解决办法不怎么优雅,并且在jdk7某个版本以后能会优化掉long padding,尼玛java程序员是有多难啊,想尽了一切办法对付jdk7中的这个优化,详情点击http://ifeve.com/false-sharing-java-7/
但是但是...开始使用jdk8的同学注意了,java8已经给出了官方的解决办法...
就是sun.misc.Contended注解http://mail.openjdk.java.net/pipermail/hotspot-dev/2012-November/007309.html
- Java view:
- // jdk8新特性,Contended注解避免false sharing
- // Restricted on user classpath
- // Unlock: -XX:-RestrictContended
- @sun.misc.Contended
- public class VolatileLong {
- volatile long v = 0L;
- }
- Memory view:
- ...–––-)(******HV******)(–––...
要注意的是user classpath使用此注解默认是无效的,需要在jvm启动时设置-XX:-RestrictContended
jdk8中已经使用sun.misc.Contended的地方:
- src/share/classes/java/util/concurrent/ConcurrentHashMap.java
- 2458: @sun.misc.Contended static final class CounterCell {
- src/share/classes/java/util/concurrent/Exchanger.java
- 310: * bookkeeping. Padded via @sun.misc.Contended to reduce m
- 313: @sun.misc.Contended static final class Node {
- src/share/classes/java/util/concurrent/ForkJoinPool.java 161:@sun.misc.Contended
- 643: @sun.misc.Contended
- src/share/classes/java/util/concurrent/atomic/Striped64.java
- 55: * (via @sun.misc.Contended) to reduce cache contention. Pa
- 119: @sun.misc.Contended static final class Cell {
- src/share/classes/java/lang/Thread.java
- 2004: @sun.misc.Contended("tlr")
- 2008: @sun.misc.Contended("tlr")
- 2012: @sun.misc.Contended("tlr")
最后,贴上测试代码,感兴趣的各自测试吧
- public class FalseSharing implements Runnable {
- public final static int NUM_THREADS = 4; // change
- public final static long ITERATIONS = 500L * 1000L * 1000L;
- private final int arrayIndex;
- private static VolatileLong3[] longs = new VolatileLong3[NUM_THREADS];
- static {
- for (int i = 0; i < longs.length; i++) {
- longs[i] = new VolatileLong3();
- }
- }
- public FalseSharing(final int arrayIndex) {
- this.arrayIndex = arrayIndex;
- }
- public static void main(final String[] args) throws Exception {
- long start = System.nanoTime();
- runTest();
- System.out.println("duration = " + (System.nanoTime() - start));
- }
- private static void runTest() throws InterruptedException {
- Thread[] threads = new Thread[NUM_THREADS];
- for (int i = 0; i < threads.length; i++) {
- threads[i] = new Thread(new FalseSharing(i));
- }
- for (Thread t : threads) {
- t.start();
- }
- for (Thread t : threads) {
- t.join();
- }
- }
- public void run() {
- long i = ITERATIONS + 1;
- while (0 != --i) {
- longs[arrayIndex].value = i;
- }
- }
- public final static class VolatileLong {
- public volatile long value = 0L;
- }
- // long padding避免false sharing
- // 按理说jdk7以后long padding应该被优化掉了,但是从测试结果看padding仍然起作用
- public final static class VolatileLong2 {
- volatile long p0, p1, p2, p3, p4, p5, p6;
- public volatile long value = 0L;
- volatile long q0, q1, q2, q3, q4, q5, q6;
- }
- // jdk8新特性,Contended注解避免false sharing
- // Restricted on user classpath
- // Unlock: -XX:-RestrictContended
- @sun.misc.Contended
- public final static class VolatileLong3 {
- public volatile long value = 0L;
- }
- }
我机器上的测试结果:
- VolatileLong: duration = 29594765000
- VolatileLong2:duration = 7234555000
- VolatileLong3:duration = 7167475000
测试代码来自http://ifeve.com/falsesharing/,稍有改动
Java8 中HashMap的优化 | Java 绕过编译器检查抛出“受检查的”异常
- 2014-04-18 13:58
- 浏览 314
- 评论(4)
- 分类:编程语言
- 相关推荐
0 0
- Java8中用sun.misc.Contended避免伪共享(false sharing)
- Java8中用sun.misc.Contended避免伪共享(false sharing)
- Java8中用sun.misc.Contended避免伪共享(false sharing)
- @sun.misc.Contended避免伪共享(false sharing)
- @sun.misc.Contended避免伪共享(false sharing)
- Java8中@Contended和伪共享
- 伪共享false sharing
- 伪共享(False Sharing)
- 伪共享(False Sharing)
- 伪共享(False Sharing)
- 伪共享(False Sharing)
- 伪共享(False Sharing)
- 伪共享(False Sharing)
- 内存伪共享(False Sharing)
- 多线程伪共享(false sharing)问题分析
- 多线程伪共享(false sharing)问题分析
- Java 伪共享(False Sharing)
- 多线程伪共享(false sharing)问题分析
- TestCpp例子大致介绍
- 苹果开发者证书的共享
- 堆排序(C语言实现)
- 图像模糊算法实现
- ASP.NET 页生命周期概述
- Java8中用sun.misc.Contended避免伪共享(false sharing)
- 对网络序和主机序的理解
- CSDN博客——从失望到希望
- PHP环境搭建后程序运行测试——怎么让程序运行起来
- STRUCK跟踪sequence下载地址
- 树莓派经常用到的软件及安装方法备忘
- Android中Context详解 ---- 你所不知道的Context
- android 按两次返回键退出
- 价值与价格的问题
评论
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
这里 为什么是 13 * 8 个字节 还不是 (7+1)*8? 8*8 =64不是正好是一个cache line么?
是15*8,因为用7个下标保证了会有56个字节填充在数值的任何一边,56字节的填充+8字节的long数值正好装进一行64字节的缓存行。
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
这里 为什么是 13 * 8 个字节 还不是 (7+1)*8? 8*8 =64不是正好是一个cache line么?
额...一个cache line确实是64
我水平有限,无法准确回答你这个问题,我说下我的测试结果吧
环境mac os,jdk1.7.15
public final static class VolatileLong2 {
// long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
long q0, q1, q2, q3, q4, q5, q6;
}
上面的代码,正好填充了一个cache line,但是会被jdk1.7.15优化掉,也就是说padding失效
而下面代码不会被优化掉,padding有效
public final static class VolatileLong2 {
long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
long q0, q1, q2, q3, q4, q5, q6;
}
我不确定但我猜测这么做是为了防止jdk1.7对padding的优化,额...只能这么猜了,水平实在有限,如果你有确切答案了,请告诉我,十分感谢
其实只要加个public就行了。
public final static class VolatileLong2 {
public volatile long value = 0L;
public long q0, q1, q2, q3, q4, q5, q6;
}
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
这里 为什么是 13 * 8 个字节 还不是 (7+1)*8? 8*8 =64不是正好是一个cache line么?
额...一个cache line确实是64
我水平有限,无法准确回答你这个问题,我说下我的测试结果吧
环境mac os,jdk1.7.15
public final static class VolatileLong2 {
// long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
long q0, q1, q2, q3, q4, q5, q6;
}
上面的代码,正好填充了一个cache line,但是会被jdk1.7.15优化掉,也就是说padding失效
而下面代码不会被优化掉,padding有效
public final static class VolatileLong2 {
long p0, p1, p2, p3, p4, p5, p6;
public volatile long value = 0L;
long q0, q1, q2, q3, q4, q5, q6;
}
我不确定但我猜测这么做是为了防止jdk1.7对padding的优化,额...只能这么猜了,水平实在有限,如果你有确切答案了,请告诉我,十分感谢
public volatile long value = 0L;
volatile long q0, q1, q2, q3, q4, q5, q6;
这里 为什么是 13 * 8 个字节 还不是 (7+1)*8? 8*8 =64不是正好是一个cache line么?