一. 背景
写代码和线上维护时,调试功能是必不可少的,经常在应用程序启动脚本中看到如下配置:
JAVA_DEBUG_OPT=" -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,address=9108,server=y,suspend=n "
这几参数中,对address相对熟悉一点,因为线上要修改端口号,而其他几个参数就不知道什么意思了。今天深入学习了一下,分享给大家。
二. 每个参数的含义
-Xdebug
让jvm在调试模式下运行,必须开启。
-Xnoagent
禁用默认的sun默认的调试器。 --- 这个参数最不解,找了很多文章,也没有解决我的疑惑,希望了解的人告诉我下哈
-Djava.compiler=NONE
禁止jvm加载JIT编译器。考虑到debug模式下都是一条一条执行指令,使用JIT效率会更低,因此禁用掉。具体的原因有点复杂,具体请看下面。
-Xrunjdwp:transport
让jvm加载jdwp(java debug wire protocol,java调试网络协议)的实现。通过jdwp,调试器和jvm进行通信。这个参数后面有一系列的参数,如transport、address等。
transport
协议传输方式,dt_sorket表示使用套接字进行传输。据说,win平台可以使用共享内存传输,没有试过。
server
debug需要调试器(eclipse是一种调试器)和目标应用程序(待调试的程序)协同工作,这样才能有效调试,因此,调试链路有两个节点:调试器和目标应用程序。这两个节点都可以作为服务端,等待对方的连接,该参数就是控制究竟哪一方作为服务端的。该参数有两种取值:
y:目标应用程序作为服务端,这是我们最常用的方式。应用程序开启debug模式,启动应用,然后调试器(eclipse)进行连接。
n:调试器作为服务端。我没有使用过这种方式,用的也不多。使用这种方式,调试器首先要开启一个listener,然后目标应用程序启动,最后,两者连接起来。
address
套接字传输的端口号,这个参数你也最熟悉了吧,因为改的最多。
suspend
是否延迟加载jvm。这个参数也有点重要,有两个取值:
y:运行程序启动脚本后,发现jvm会暂停,直到调试器连接过来后,jvm会继续加载。一般,类的init的方法在加载时运行,你可以通过设置suspend=y来调试init方法,比如filter的init方法等。
n:运行程序启动脚本后,jvm自动加载,然后开启debug端口号。调试器随时连接到jvm。一般都会使用这种方式。
三. 一些调试技巧
3.1 类的init方法通常在加载时运行,如何调试?
修改debug参数,修改suspend=y,重启应用程序。这样,只有调试器连接应用程序后,jvm才会继续加载。
3.2 如何修改debug端口号?
有时碰到端口冲突的问题,修改address参数即可。
四. 为什么设置-Djava.compiler=NONE
设置目的:禁止使用JIT转义器,加快debug的速度。
jvm在执行class文件时,会将java字节码转换成本地机器码,有两种利器:JIT和转义器(interpreter)。
转义器(interpreter):每次执行class文件,jvm都会将字节码转换成微指令,然后按照顺序依次执行,有时一个java指令会被转义成十几个微指令。因此执行效率非常低。
JIT:针对interpreter的瓶颈,JIT技术在第一次执行class文件时,对其进行一次编译,会被精简成原生态指令码。经过一次编译后,下次再执行相同的class时,就不需要编译了。因此,对频繁执行的class使用JIT技术进行转义,整体执行效率会非常高。PS:由于第一次执行时,要进行彻底转义,因此会非常耗时。
因此,interpreter和JIT各有优缺点:
interpreter:针对执行次数极少的class文件,interpreter性能更好。
JIT:对于频繁执行的class文件,JIT技术更优。
学习完interpreter和JIT后,回到debug模式下为什么要关闭JIT?一般来说,debug模式下,都是执行几行代码,不会频繁执行某一个class。因此为了提高debug的速度,一般把JIT关闭,使用interpreter转义java指令即可。