最近再次翻netty和disrupt的源码, 发现一些地方使用AtomicXXX.lazySet()/unsafe.putOrderedXXX系列, 以前一直没有注意lazySet这个方法, 仔细研究一下发现很有意思。我们拿AtomicReferenceFieldUpdater的set()和lazySet()作比较, 其他AtomicXXX类和这个类似。
1 public void set(T obj, V newValue) { 2 // ... 3 unsafe.putObjectVolatile(obj, offset, newValue); 4} 5 6 public void lazySet(T obj, V newValue) { 7 // ... 8 unsafe.putOrderedObject(obj, offset, newValue); 9}1.首先set()是对volatile变量的一个写操作, 我们知道volatile的write为了保证对其他线程的可见性会追加以下两个Fence(内存屏障) 1)StoreStore // 在intel cpu中, 不存在[写写]重排序, 这个可以直接省略了 2)StoreLoad // 这个是所有内存屏障里最耗性能的 注: 内存屏障相关参考Doug Lea大大的cookbook (http://g.oswego.edu/dl/jmm/cookbook.html)
2.Doug Lea大大又说了, lazySet()省去了StoreLoad屏障, 只留下StoreStore 在这里 http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6275329 把最耗性能的StoreLoad拿掉, 性能必然会提高不少(虽然不能禁止写读的重排序了保证不了可见性, 但给其他应用场景提供了更好的选择, 比如上边连接中Doug Lea举例的场景)。
但是但是, 在好奇心驱使下我翻了下JDK的源码(unsafe.cpp):
01// 这是unsafe.putObjectVolatile() 02 UNSAFE_ENTRY(void, Unsafe_SetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) 03 UnsafeWrapper("Unsafe_SetObjectVolatile"); 04 oop x = JNIHandles::resolve(x_h); 05 oop p = JNIHandles::resolve(obj); 06 void* addr = index_oop_from_field_offset_long(p, offset); 07 OrderAccess::release(); 08 if (UseCompressedOops) { 09 oop_store((narrowOop*)addr, x); 10 } else { 11 oop_store((oop*)addr, x); 12 } 13 OrderAccess::fence(); 14UNSAFE_END 15 16// 这是unsafe.putOrderedObject() 17 UNSAFE_ENTRY(void, Unsafe_SetOrderedObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h)) 18 UnsafeWrapper("Unsafe_SetOrderedObject"); 19 oop x = JNIHandles::resolve(x_h); 20 oop p = JNIHandles::resolve(obj); 21 void* addr = index_oop_from_field_offset_long(p, offset); 22 OrderAccess::release(); 23 if (UseCompressedOops) { 24 oop_store((narrowOop*)addr, x); 25 } else { 26 oop_store((oop*)addr, x); 27 } 28 OrderAccess::fence(); 29UNSAFE_END仔细看代码是不是有种被骗的感觉, 他喵的一毛一样啊. 难道是JIT做了手脚?生成汇编看看
生成assembly code需要hsdis插件
mac平台从这里下载 https://kenai.com/projects/base-hsdis/downloads/directory/gnu-versions
linux和windows可以从R大的[高级语言虚拟机圈子]下载 http://hllvm.group.iteye.com/
为了测试代码简单, 使用AtomicLong来测:
01// set() 02 public class LazySetTest { 03 private static final AtomicLong a = new AtomicLong(); 04 05 public static void main(String[] args) { 06 for (int i = 0; i < 100000000; i++) { 07 a.set(i); 08 } 09 } 10} 11 12// lazySet() 13 public class LazySetTest { 14 private static final AtomicLong a = new AtomicLong(); 15 16 public static void main(String[] args) { 17 for (int i = 0; i < 100000000; i++) { 18 a.lazySet(i); 19 } 20 } 21}分别执行以下命令:
01 1.export LD_LIBRARY_PATH=~/hsdis插件路径/ 02 2.javac LazySetTest.java && java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LazySetTest 03 04// ------------------------------------------------------ 05// set()的assembly code片段: 06 0x000000010ccbfeb3: mov %r10,0x10(%r9) 07 0x000000010ccbfeb7: lock addl $0x0,(%rsp) ;*putfield value 08 ; - java.util.concurrent.atomic.AtomicLong::set@2 (line 112) 09 ; - LazySetTest::main@13 (line 13) 10 0x000000010ccbfebc: inc