<address id="xhxt1"><listing id="xhxt1"></listing></address><sub id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></sub>

    <thead id="xhxt1"><dfn id="xhxt1"><ins id="xhxt1"></ins></dfn></thead>

    JUC中Atomic class之lazySet的一点疑惑

    最近再次翻netty和disrupt的源码, 发现一些地方使用AtomicXXX.lazySet()/unsafe.putOrderedXXX系列, 以前一直没有注意lazySet这个方法, 仔细研究一下发现很有意思。我们拿AtomicReferenceFieldUpdater的set()和lazySet()作比较, 其他AtomicXXX类和这个类似。

    public void set(T obj, V newValue) {
        // ...
        unsafe.putObjectVolatile(obj, offset, newValue);
    }
    
    public void lazySet(T obj, V newValue) {
        // ...
        unsafe.putOrderedObject(obj, offset, newValue);
    }
    

    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):

    // 这是unsafe.putObjectVolatile()
    UNSAFE_ENTRY(void, Unsafe_SetObjectVolatile(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h))
        UnsafeWrapper("Unsafe_SetObjectVolatile");
        oop x = JNIHandles::resolve(x_h);
        oop p = JNIHandles::resolve(obj);
        void* addr = index_oop_from_field_offset_long(p, offset);
        OrderAccess::release();
        if (UseCompressedOops) {
            oop_store((narrowOop*)addr, x);
        } else {
            oop_store((oop*)addr, x);
        }
        OrderAccess::fence();
    UNSAFE_END
    
    // 这是unsafe.putOrderedObject()
    UNSAFE_ENTRY(void, Unsafe_SetOrderedObject(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jobject x_h))
        UnsafeWrapper("Unsafe_SetOrderedObject");
        oop x = JNIHandles::resolve(x_h);
        oop p = JNIHandles::resolve(obj);
        void* addr = index_oop_from_field_offset_long(p, offset);
        OrderAccess::release();
        if (UseCompressedOops) {
            oop_store((narrowOop*)addr, x);
        } else {
            oop_store((oop*)addr, x);
        }
        OrderAccess::fence();
    UNSAFE_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来测:

    // set()
    public class LazySetTest {
        private static final AtomicLong a = new AtomicLong();
    
        public static void main(String[] args) {
            for (int i = 0; i < 100000000; i++) {
                a.set(i);
            }
        }
    }
    
    // lazySet()
    public class LazySetTest {
        private static final AtomicLong a = new AtomicLong();
    
        public static void main(String[] args) {
            for (int i = 0; i < 100000000; i++) {
                a.lazySet(i);
            }
        }
    }
    

    分别执行以下命令:

    1.export LD_LIBRARY_PATH=~/hsdis插件路径/
    2.javac LazySetTest.java && java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly LazySetTest
    
    // ------------------------------------------------------
    // set()的assembly code片段:
    0x000000010ccbfeb3: mov    %r10,0x10(%r9)
    0x000000010ccbfeb7: lock addl $0x0,(%rsp)     ;*putfield value
                                                ; - java.util.concurrent.atomic.AtomicLong::set@2 (line 112)
                                                ; - LazySetTest::main@13 (line 13)
    0x000000010ccbfebc: inc    %ebp               ;*iinc
                                                ; - LazySetTest::main@16 (line 12)
    // ------------------------------------------------------
    // lazySet()的assembly code片段:
    0x0000000108766faf: mov    %r10,0x10(%rcx)    ;*invokevirtual putOrderedLong
                                                ; - java.util.concurrent.atomic.AtomicLong::lazySet@8 (line 122)
                                                ; - LazySetTest::main@13 (line 13)
    0x0000000108766fb3: inc    %ebp               ;*iinc
                                                ; - LazySetTest::main@16 (line 12)
    

    好吧, set()生成的assembly code多了一个lock前缀的指令

    查询IA32手册可知道, lock addl $0x0,(%rsp)其实就是StoreLoad屏障了, 而lazySet()确实没生成StoreLoad屏障

    这里JIT除了将方法内联, 相同代码生成不同指令是怎么做到的?

    http://hg.openjdk.java.net/jdk7u/jdk7u/hotspot/file/6e9aa487055f/src/share/vm/classfile/vmSymbols.hpp

    查看如上代码, 812行和868行分别有如下代码:

    do_intrinsic(_putObjectVolatile,        sun_misc_Unsafe,        putObjectVolatile_name, putObject_signature,   F_RN)
    do_intrinsic(_putOrderedObject,         sun_misc_Unsafe,        putOrderedObject_name, putOrderedObject_signature, F_RN)
    

    putObjectVolatile与putOrderedObject都在vmSymbols.hpp的宏定义中,jvm会根据instrinsics id生成特定的指令集 putObjectVolatile与putOrderedObject生成的汇编指令不同估计是源于这里了, 继续往下看 hotspot/src/share/vm/opto/libaray_call.cpp这个类:
    首先看如下两行代码:

    case vmIntrinsics::_putObjectVolatile:        return inline_unsafe_access(!is_native_ptr,  is_store, T_OBJECT,   is_volatile);
    case vmIntrinsics::_putOrderedObject:         return inline_unsafe_ordered_store(T_OBJECT);
    

    再看inline_unsafe_access()和inline_unsafe_ordered_store(), 不贴出全部代码了, 只贴出重要的部分:

    bool LibraryCallKit::inline_unsafe_ordered_store(BasicType type) {
      // This is another variant of inline_unsafe_access, differing in
      // that it always issues store-store ("release") barrier and ensures
      // store-atomicity (which only matters for "long").
    
      // ...
      if (type == T_OBJECT) // reference stores need a store barrier.
        store = store_oop_to_unknown(control(), base, adr, adr_type, val, type);
      else {
        store = store_to_memory(control(), adr, val, type, adr_type, require_atomic_access);
      }
      insert_mem_bar(Op_MemBarCPUOrder);
      return true;
    }
    
    ---------------------------------------------------------------------------------------------------------
    
    bool LibraryCallKit::inline_unsafe_access(bool is_native_ptr, bool is_store, BasicType type, bool is_volatile) {
      // ....
    
      if (is_volatile) {
        if (!is_store)
          insert_mem_bar(Op_MemBarAcquire);
        else
          insert_mem_bar(Op_MemBarVolatile);
      }
    
      if (need_mem_bar) insert_mem_bar(Op_MemBarCPUOrder);
    
      return true;
    }
    

    我们可以看到 inline_unsafe_access()方法中, 如果是is_volatile为true, 并且是store操作的话, 有这样的一句代码 insert_mem_bar(Op_MemBarVolatile), 而inline_unsafe_ordered_store没有插入这句代码

    再继续看/hotspot/src/cpu/x86/vm/x86_64.ad的membar_volatile

    instruct membar_volatile(rFlagsReg cr) %{
      match(MemBarVolatile);
      effect(KILL cr);
      ins_cost(400);
    
      format %{
        $$template
        if (os::is_MP()) {
          $$emit$$"lock addl [rsp + #0], 0\t! membar_volatile"
        } else {
          $$emit$$"MEMBAR-volatile ! (empty encoding)"
        }
      %}
      ins_encode %{
        __ membar(Assembler::StoreLoad);
      %}
      ins_pipe(pipe_slow);
    %}
    

    lock addl [rsp + #0], 0\t! membar_volatile指令原来来自这里

    总结:
    错过一些细节, 但在主流程上感觉是有一点点明白了, 有错误之处请指正

    参考了以下资料:
    1.http://g.oswego.edu/dl/jmm/cookbook.html
    2.https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly
    3.http://www.quora.com/How-does-AtomicLong-lazySet-work
    4.http://bad-concurrency.blogspot.ru/2012/10/talk-from-jax-london.html

    原创文章,转载请注明: 转载自并发编程网 – www.gofansmi6.com本文链接地址: JUC中Atomic class之lazySet的一点疑惑


    FavoriteLoading添加本文到我的收藏
    • Trackback 关闭
    • 评论 (4)
      • szhnet
      • 2016/11/01 3:54下午

      学习了

      • michaelchan
      • 2017/06/05 6:52上午

      厉害

      • michaelchan
      • 2017/06/06 6:38上午

      volatile write是不能与之前的读和写重排序的。按道理,应该在volatile write之前,增加LoadStore和StoreStore。

      为什么没有这里的LoadStore?

      • 加多
      • 2017/06/18 11:54上午

      楼主求 写个unsafe的帖子,介绍下里面的函数

    您必须 登陆 后才能发表评论

    return top

    爱投彩票 hxb| 66t| hjx| 6jt| zt6| hth| l6n| dxj| 4vh| tz5| hfr| l5x| htj| xlf| 5nf| vz5| nrh| z5z| pvt| 3rl| dz4| xtf| r4z| fzl| 4vx| zn4| zd4| rpt| j4p| rvp| f5j| pbn| 3vj| pn3| dpt| p3t| lzt| 3vp| th4| jf4| dzl| d4v| xlx| 2dp| tf2| zvp| f2x| txb| 3zb| nt3| djn| p3v| z3l| dxl| 3lz| bn1| tjd| z2f| plp| n2j|