文档章节

Java volatile 汇编代码研究

-10
 -10
发布于 2015/02/04 17:52
字数 1270
阅读 50
收藏 0

一个程序

我们有下面这段程序代码:

<pre> package edu.hushi.jvm; /** * * @author -10 * */ public class VisibilityTest extends Thread { private boolean stop; public void run() { int i = 0; while(!stop) { i++; } System.out.println("finish loop,i=" + i); } public void stopIt() { stop = true; } public boolean getStop(){ return stop; } public static void main(String[] args) throws Exception { VisibilityTest v = new VisibilityTest(); v.start(); Thread.sleep(1000); v.stopIt(); Thread.sleep(2000); System.out.println("finish main"); System.out.println(v.getStop()); } } </pre>

程序比较简单,在主线程中启动一个线程,这个线程不停的对局部变量做自增操作,主线程休眠 1 秒中后改变启动线程的循环控制变量,想让它停止循环。这个程序在 client 模式下是能停止线程做自增操作的,但是在 server 模式先将是无限循环。若是改成

<pre> private volatile boolean stop; </pre>

用 volatile 修饰 stop 变量,将不会出现死循环。我们知道 volatile 在 JVM 内存模型中是保证修饰变量的可见性,这个不是我们今天讨论的重点,我们今天想看看在 volatile 修饰下和不修饰代码编译成的汇编代码的区别,以便我们学习 JVM 的内存模型。

HSDIS 介绍

首先我们介绍一个工具,HSDIS是由Project Kenai提供并得到Sun官方推荐的HotSpot VM JIT编译代码的反汇编插件,作用是让HotSpot的-XX:+PrintAssembly指令调用它来把动态生成的本地代码还原为汇编代码输出,同时还生成了大量非常有价值的注释,这样我们就可以通过输出的代码来分析问题。读者可以根据自己的操作系统和CPU类型从Kenai的网站上下载编译好的插件,直接放到JDK_HOME/jre/bin/client和JDK_HOME/jre/bin/server目录中即可。如果没有找到所需操作系统(譬如Windows的就没有)的成品,那就得自己拿源码编译一下,或者去HLLVM圈子中下载也可以,这里也有 win32 和 win64 编译好的。

<pre> -server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=dontinline,*VisibilityTest.run -XX:CompileCommand=compileonly,*VisibilityTest.run -XX:+PrintAssembly </pre>

其中

  • -Xcomp 参数-Xcomp是让虚拟机以编译模式执行代码,这样代码可以偷懒,不需要执行足够次数来预热都能触发JIT编译。
  • -XX:CompileCommand=dontinline,*VisibilityTest.run 这个表示不要把 run 方法给内联了,这是解决内联问题。
  • -XX:CompileCommand=compileonly,*VisibilityTest.run 这个表示只编译 run 方法,这样的话只会输出sum方法的ASM码。
  • -XX:+UnlockDiagnosticVMOptions 这个参数是和 -XX:+PrintAssembly 一起才能生效答应汇编代码

若果一切顺利将可以输出 assembly 代码,但是我研究了一段时间,还是没有能够看懂这些代码,如果有网友能够解释得通上面说的两种现象,可以告诉我。

assembly

以下是 没有 volatile 修饰的 assembly 代码

<pre class="nowordwrap"> Java HotSpot(TM) Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output CompilerOracle: dontinline *VisibilityTest.run CompilerOracle: compileonly *VisibilityTest.run Loaded disassembler from D:\Dev\Java\jdk1.7.0_25\jre\bin\server\hsdis-i386.dll Decoding compiled method 0x0193be88: Code: Argument 0 is unknown.RIP: 0x193bf80 Code size: 0x00000050 [Disassembling for mach='i386'] [Entry Point] [Constants] # {method} 'run' '()V' in 'edu/hushi/jvm/VisibilityTest' # [sp+0x10] (sp of caller) 0x0193bf80: cmp eax,dword ptr [ecx+4h] 0x0193bf83: jne 191d100h ; {runtime_call} 0x0193bf89: nop [Verified Entry Point] 0x0193bf8c: mov dword ptr [esp+0ffffc000h],eax 0x0193bf93: push ebp 0x0193bf94: sub esp,8h ;*synchronization entry ; - edu.hushi.jvm.VisibilityTest::run@-1 (line 13) 0x0193bf97: mov ebp,ecx 0x0193bf99: movzx eax,byte ptr [ecx+64h] ;*getfield stop ; - edu.hushi.jvm.VisibilityTest::run@9 (line 14) 0x0193bf9d: test eax,eax 0x0193bf9f: jne 193bfafh ;*ifeq ; - edu.hushi.jvm.VisibilityTest::run@12 (line 14) 0x0193bfa1: mov ebx,1h ; OopMap{ebp=Oop off=38} ;*ifeq ; - edu.hushi.jvm.VisibilityTest::run@12 (line 14) 0x0193bfa6: test dword ptr [0a0000h],edi ;*ifeq ; - edu.hushi.jvm.VisibilityTest::run@12 (line 14) ; {poll} 0x0193bfac: inc ebx ;*iinc ; - edu.hushi.jvm.VisibilityTest::run@5 (line 15) 0x0193bfad: jmp 193bfa6h 0x0193bfaf: mov ecx,14h 0x0193bfb4: nop 0x0193bfb7: call 191dd00h ; OopMap{ebp=Oop off=60} ;*getstatic out ; - edu.hushi.jvm.VisibilityTest::run@15 (line 17) ; {runtime_call} 0x0193bfbc: int3 ;*iinc ; - edu.hushi.jvm.VisibilityTest::run@5 (line 15) 0x0193bfbd: int3 0x0193bfbe: hlt 0x0193bfbf: hlt [Exception Handler] [Stub Code] 0x0193bfc0: jmp 1938780h ; {no_reloc} [Deopt Handler Code] 0x0193bfc5: push 193bfc5h ; {section_word} 0x0193bfca: jmp 191e280h ; {runtime_call} 0x0193bfcf: hlt finish main true </pre>

以下是 volatile 修饰的 assembly 代码

<pre class="nowordwrap"> Java HotSpot(TM) Server VM warning: PrintAssembly is enabled; turning on DebugNonSafepoints to gain additional output CompilerOracle: dontinline *VisibilityTest.run CompilerOracle: compileonly *VisibilityTest.run Loaded disassembler from D:\Dev\Java\jdk1.7.0_25\jre\bin\server\hsdis-i386.dll Decoding compiled method 0x01c7c688: Code: Argument 0 is unknown.RIP: 0x1c7c780 Code size: 0x00000050 [Disassembling for mach='i386'] [Entry Point] [Constants] # {method} 'run' '()V' in 'edu/hushi/jvm/VisibilityTest' # [sp+0x10] (sp of caller) 0x01c7c780: cmp eax,dword ptr [ecx+4h] 0x01c7c783: jne 1c5d100h ; {runtime_call} 0x01c7c789: nop [Verified Entry Point] 0x01c7c78c: mov dword ptr [esp+0ffffc000h],eax 0x01c7c793: push ebp 0x01c7c794: sub esp,8h ;*synchronization entry ; - edu.hushi.jvm.VisibilityTest::run@-1 (line 13) 0x01c7c797: movzx eax,byte ptr [ecx+64h] ;*getfield stop ; - edu.hushi.jvm.VisibilityTest::run@9 (line 14) 0x01c7c79b: xor ebp,ebp 0x01c7c79d: test eax,eax 0x01c7c79f: jne 1c7c7b0h ;*iinc ; - edu.hushi.jvm.VisibilityTest::run@5 (line 15) 0x01c7c7a1: movzx ebx,byte ptr [ecx+64h] ;*getfield stop ; - edu.hushi.jvm.VisibilityTest::run@9 (line 14) 0x01c7c7a5: inc ebp ; OopMap{ecx=Oop off=38} ;*ifeq ; - edu.hushi.jvm.VisibilityTest::run@12 (line 14) 0x01c7c7a6: test dword ptr [350000h],edi ; {poll} 0x01c7c7ac: test ebx,ebx 0x01c7c7ae: je 1c7c7a1h ;*getstatic out ; - edu.hushi.jvm.VisibilityTest::run@15 (line 17) 0x01c7c7b0: mov ecx,14h 0x01c7c7b5: nop 0x01c7c7b7: call 1c5dd00h ; OopMap{off=60} ;*getstatic out ; - edu.hushi.jvm.VisibilityTest::run@15 (line 17) ; {runtime_call} 0x01c7c7bc: int3 ;*getstatic out ; - edu.hushi.jvm.VisibilityTest::run@15 (line 17) 0x01c7c7bd: hlt 0x01c7c7be: hlt 0x01c7c7bf: hlt [Exception Handler] [Stub Code] 0x01c7c7c0: jmp 1c78f80h ; {no_reloc} [Deopt Handler Code] 0x01c7c7c5: push 1c7c7c5h ; {section_word} 0x01c7c7ca: jmp 1c5e280h ; {runtime_call} 0x01c7c7cf: hlt finish loop,i=1109307815 finish main true </pre>

以上测试环境为

  • win7 32
  • Java(TM) SE Runtime Environment (build 1.7.0_25-b17)
  • Pentium(R) Dual-Core CPU E5400 @ 2.70GHz

http://hushi55.github.io/2015/01/05/volatile-assembly/

参考

© 著作权归作者所有

-10

-10

粉丝 10
博文 10
码字总数 14996
作品 0
深圳
高级程序员
私信 提问
C++雾中风景13:volatile解惑

笔者入职百度时,二面面试官的让我聊聊C++之中的volatile关键词。volatile在Java和C++之中的差别可谓是天差地别,我只是简单聊了聊Java之中的volatile,面试官对我的回答并不满意。后续学习《...

LeeHappen
03/19
0
0
Java并发机制底层实现原理-volatile

章节目录 volatile的实现原理与应用 1.volatile的实现原理与应用 Java source code->Java class->JVM->汇编指令->cpu执行 java中使用的并发机制依赖于JVM实现和cpu指令。 1.1 volatile应用 ...

markfork
2018/04/29
0
0
如何封锁您的(或打开别人的) Java 代码

无论是修改许多网上源码库中的代码,还是调用常见的操作系统例行程序,您免不了要花一些时间去琢磨您没有编写过的代码,而且您还可能没有这些代码的源文件。在开始调试代码时,您需要有一个好...

鹏凌三千
2009/05/26
273
0
《Java并发编程的艺术》第二章--2.1--volatile的定义与实现原理

前言 Java代码在编译后会变成Java字节码,字节码被类加载器加载到JVM里,JVM执行字节 码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和 CPU的指令。 2.1 v...

我是警察叔叔
09/11
63
0
多年C#,学习php一天的感觉。

一个兄弟的帖子:多年java,学习php一周感觉。 上学的时候,学习汇编程序开发,用汇编开发LED指示灯的功能,感觉这个太牛X了。 >>这个时期,对什么是低级语言和高级语言没有什么概念。 后来,...

生命周期
2016/02/16
2.6K
11

没有更多内容

加载失败,请刷新页面

加载更多

你知道多少this,new,bind,call,apply?那我告诉你

那么什么是this,new,bind,call,apply呢?这些你都用过吗?掌握这些内容都是基础中的基础了。如果你不了解,那还不赶快去复习复习,上网查阅资料啥的! 通过call,apply,bind可以改变thi...

达达前端小酒馆
今天
4
0
设计模式之命令模式

命令模式的类图 其中的角色有: Client 客户端。只依赖于调用者Invoker、接收者Receiver、以及Command(网上找的图片这里没有画出来),不用关注接收者如何执行命令,只需要告诉调用者需要执行...

陈年之后是青葱
今天
7
0
2. 彤哥说netty系列之IO的五种模型

你好,我是彤哥,本篇是netty系列的第二篇。 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识。 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别。 何...

彤哥读源码
今天
5
0
OSChina 周四乱弹 —— 喵的波粒二象性

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 小小编辑推荐:《水墨兰亭》- 李志辉 《水墨兰亭》- 李志辉 手机党少年们想听歌,请使劲儿戳(这里) @巴拉迪维 :卧室里采光要足够好,这样...

小小编辑
今天
34
1
前后端分离接口规范

最近在开发,遇到前后端关于Boolean类型的参数传参和接收的问题: 场景:后台会根据用户是否出车/是否出司机(Boolean类型)来决定后端的业务逻辑(比如费用的计算),前端使用JSON字符串类型...

code-ortaerc
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部