如何查看JIT的反汇编
java执行的时候加上:
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly
选项。
可能会报错:
Could not load hsdis-amd64.dll; library not loadable; PrintAssembly is disabled
原因是没有hsdis-amd64.dll库,可以从SO里获得预编译好的dll,参看:
https://stackoverflow.com/questions/1503479/how-to-see-jit-compiled-code-in-jvm
answer里也给出了windows下手动编译出hsdis-amd64.dll库的方法,可以试试。
将dll拷贝到:
Java\jdk1.8.0_72\jre\bin\server
下即可,因为我使用的是1.8.0_72 server版,所以这里放到了对应的jdk的jre\bin\server下。
再次执行:
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly …
即可打印出大量的汇编码。
一般说来,一个函数调用达到一定的次数才可能被JIT,所以,为了测试如下函数:
private static volatile int race_ = 0;
private static void myincr()
{
race_++;
}
我们在main里循环调用了1000次:
public static void main(String[] args)
{
for(int i=0; i<1000; ++i)
{
myincr();
}
}
之后从生成的汇编里搜索myincr,得到:
# {method} {0x0000000016cb3500} 'myincr' '()V' in 'com/lee/MainFlow'
# [sp+0x40] (sp of caller)
0x000000000257ce60: mov dword ptr [rsp+0ffffffffffffa000h],eax
0x000000000257ce67: push rbp
0x000000000257ce68: sub rsp,30h
0x000000000257ce6c: mov rsi,16cfcf20h ; {metadata(method data for {method} {0x0000000016cb3500} 'myincr' '()V' in 'com/lee/MainFlow')}
0x000000000257ce76: mov edi,dword ptr [rsi+0dch]
0x000000000257ce7c: add edi,8h
0x000000000257ce7f: mov dword ptr [rsi+0dch],edi
0x000000000257ce85: mov rsi,16cb34f8h ; {metadata({method} {0x0000000016cb3500} 'myincr' '()V' in 'com/lee/MainFlow')}
0x000000000257ce8f: and edi,1ff8h
0x000000000257ce95: cmp edi,0h
0x000000000257ce98: je 257cec7h
0x000000000257ce9e: mov rsi,0d59c01b0h ; {oop(a 'java/lang/Class' = 'com/lee/MainFlow')}
0x000000000257cea8: mov edi,dword ptr [rsi+88h] ;*getstatic race_
; - com.lee.MainFlow::myincr@0 (line 59)
0x000000000257ceae: inc edi
0x000000000257ceb0: mov dword ptr [rsi+88h],edi
0x000000000257ceb6: lock add dword ptr [rsp],0h ;*putstatic race_
; - com.lee.MainFlow::myincr@5 (line 59)
0x000000000257cebb: add rsp,30h
0x000000000257cebf: pop rbp
0x000000000257cec0: test dword ptr [150100h],eax ; {poll_return}
我们也可以只打印关注的函数myincr的汇编码,使用:
-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,com.lee.MainFlow::myincr
即可。
据说可以加上-Xcomp选项强制JIT而无需循环调用预热,可以试试。
远程调试
远程debug的意思是启动一个Java进程,启动一个debugger进程,将两者连接起来,利用debugger来debug Java进程。
事实上目前所有的IDE的debug功能都是通过远程debug方式来实现的,它们都利用了一个叫做JDPA(Java Platform Debugger Architecture)的技术。
利用JDPA我们除了能够在IDE开发的时候debug,也能够将IDE attach到一个生产环境上正在运行的Java进程做debug(事实上这两个场景在本质上是一样的)。
一般这样指定JDPA:
-Xdebug -Xrunjdwp:transport=dt_socket,address=38000,server=y,suspend=n
suspend的含义:suspend=n 用来告知 JVM 立即执行,不要等待未来将要连上(attached)的调试者。如果设成 y, 则应用将暂停不运行,直到有调试者连接上。suspend=y主要用来定位服务启动失败问题!
transport:有2种方式,dt_socket通过网络连接调试,dt_shmem 则使用共享内存方式调试。
onthrow :直到JVM抛出指定类型的异常才启动JDWP的初始化。由于JDWP初始化包括了连接建立,因此除非异常抛出,否则远程调试服务器不会启动。