Arthas调试与JVM参数的使用

Arthas是阿里巴巴开源的Java应用诊断工具,无需重启服务即可在线排查问题,实时监控JVM状态。它支持命令行交互,提供dashboard、thread、watch、trace等命令用于调试和监控。此外,文章还介绍了JVM的内存区域和分代收集算法,以及JVM参数调优的策略,包括堆大小、新生代与年老代的比例、垃圾收集器选择等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Arthas
arthas中文直译过来叫阿尔萨斯,是阿里巴巴开源的java应用诊断工具,在线排查问题,无需重启服务,实时监控jvm状态。支持JDK6以上版本,支持Linux/Mac/Window。

采用命令行交换方式,tab键命令自动补全,而且兼容linux系统部分命令,如:grep,ctrl+l清屏,ctrl+a跳到行首,ctrl+c终止命令等

如果你还在使用jdk原生jstat、jmap等命令排查问题,那么,强烈建议你使用arthas。

一、arthas调试线上环境bug(简直不要太优雅)

下载安装

arthas安装分为快速安装和全量安装,快速安装只下载arthas-boot.jar。全量安装是下载一个压缩包,压缩包中包含arthas-boot.jar、测试demo和封装好的启动脚本等文件,一般我们使用快速安装就可以了

快速安装

linux系统安装可以用curl命令从阿里云官方网站下载

curl -O https://arthas.aliyun.com/arthas-boot.jar 

windows系统安装用浏览器打开如下地址会提示下载

https://arthas.aliyun.com/arthas-boot.jar 

全量安装

linux全量安装需要先下载执行脚本

curl -L https://arthas.aliyun.com/install.sh | sh 

下载完成后会在当前目录生成as.sh脚本,执行脚本 ./as.sh 后会自动下载全量安装包,默认保存在~/.arthas/lib目录下。修改as.sh脚本可以更改默认保存路径
修改默认保存路径
windows全量安装只需要用浏览器打开下载地址就会提示下载

https://arthas.aliyun.com/download/latest_version?mirror=aliyun 

全量安装包的文件夹名称为arthas,目录结构如下
arthas目录结构

启动

启动之前需要明确要监听哪个java程序,并找到这个java程序的进程id。当前登录用户必须对要监听的进程有可操作权限,否则会报错!

linux下可以使用ps或jps命令可以找到java程序的进程id

ps -ef | grep java jps -mlvV  

windows下打开任务管理器可以找到对应程序的进程id
任务管理器
执行命令启动arthas

java -jar arthas-boot.jar 

启动后会提示选择监听哪个进程id,输入对应的序号即可。我要监听的进程id是19646,也就是序号2
监听进程
输入2之后敲回车,启动成功如下图
启动成功
启动时可以直接指定pid,就不用在启动过程中再输入序号了

java -jar arthas-boot.jar 19646 

如果安装时选择了全量安装,那么也可以通过脚本启动,linux系统使用as.sh脚本,windows系统使用as.bat脚本

dashbord

dashboard命令可以查看cpu、线程状态,内存信息、GC情况和jdk版本等信息
dashbord
图中字段解读

ID: Java中的线程ID,注意这个ID不能跟jstack中的nativeID一一对应 
NAME: 线程名称 
GROUP: 线程组名称 
PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高 
STATE: 线程的状态 
CPU%: 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。 
TIME: 线程运行总时间,格式为 分:秒 
INTERRUPTED: 线程当前的中断状态 
DAEMON: 是否是守护线程 

监控页面会实时刷新,默认每5000毫秒(5秒)刷新一次。可以通过 - i 参数指定刷新频率,-n
参数指定刷新次数。这个统计会有一定的开销,从截图中也可以看到arthas的cpu占比比较大,所以刷新频率不要太高,建议5秒以上,刷新次数建议10次以内

// 每10秒刷新一次,3次后停止 
dashboard -i 10000 -n 3 

thread

使用thread命令可以查看线程的状态,查看线程的堆栈
线程状态
thread命令可以追加参数

thread命令可以追加参数
id:可以查看指定线程id的堆栈信息 
-n value:找出最忙的value个线程,并打印堆栈信息 
-b:找出当前正在阻塞其他线程的线程 
-i value:指定采样cpu占比的时间间隔,默认为100ms。 

打印线程id=24的堆栈信息
打印线程堆栈信息
打印当前最忙的3个线程的堆栈信息
打印线程堆栈信息
打印正在阻塞其他线程的线程
打印线程

指定统计线程cpu占比的时间间隔,单位:毫秒。因为统计cpu占比会有一定的资源消耗,所以时间间隔不要太小,建议100毫秒以上

thead -i 200 

watch

class-pattern	类名表达式匹配
method-pattern	函数名表达式匹配
express	观察表达式,默认值:{params, target, returnObj}
condition-express	条件表达式
[b]	在函数调用之前观察
[e]	在函数异常之后观察
[s]	在函数返回之后观察
[f]	在函数结束之后(正常返回和异常返回)观察
[E]	开启正则表达式匹配,默认为通配符匹配
[x:]	指定输出结果的属性遍历深度,默认为 1

watch参数说明

trace

参数与watch相似

class-pattern	类名表达式匹配
method-pattern	方法名表达式匹配
condition-express	条件表达式
[E]	开启正则表达式匹配,默认为通配符匹配
[n:]	命令执行次数
#cost	方法执行耗时
trace com.zhongjyuan.business.controller.authController sso

tt

方法执行数据的时空隧道

-t	记录每次执行的情况
-n	限制要记录的次数,防止内存溢出
tt -t com.zhongjyuan.business.controller.authController sso

tt

ognl

通过ognl直接调用我们的代码

ognl '@com.demo.TestDemo@get()'

如果是调用spring bean中的方法只需要调用获取bean的静态方法即可

ognl '@com.demo.SpringContextHolder@getBean("mobileLoginSuccessHandler").defaultAuthorizationServerTokenServices'

getstatic

通过getstatic命令可以方便的查看类的静态属性

getstatic 类名 属性名

#显示demo.MathGame类中静态属性random
getstatic demo.MathGame random

sm

命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到。
显示String类加载的方法

sm java.lang.String

sm
显示String中的toString方法详细信息

sm -d java.lang.String toString

monitor

monitor 用来监视一个时间段中指定方法的执行次数,成功次数,失败次数,耗时等这些信息

monitor -c 5
com.zhongjyuan.business.controller.authController sso

monitor

jad

使用jad可以对代码进行反编译,反编译过来的代码可能会有语法错误,但不影响阅读。比较贴心的是,反编译过来的代码语法高亮,方便阅读
反编译
类名支持全路径,同时也支持模糊匹配,下面两个命令可以达到相同的效果

jad com.zhongjyuan.demo.ThreadBlock 
jad *ThreadBlock 

也可以对类中的某一个方法进行反编译,只需要在类名后面加上方法名即可
方法反编译

反编译过来的代码默认携带ClassLoader信息,使用 --source-only 选项可以去除ClassLoader信息
去除ClassLoader信息

jad/mc

使用jad反编译com.zhongjyuan.business.controller.authController输出到/opt/app/modules/authController.java

jad --source-only  com.zhongjyuan.business.controller.authController > /opt/app/modules/authController.java

按上面的代码编辑完毕以后,使用mc内存中对新的代码编译

mc /opt/app/modules/authController.java -d /opt/app/modules

使用redefine命令加载新的字节码

redefine /opt/app/modules/authController.class

cls

清空当前屏幕区域

jvm

查看当前 JVM 的信息
jvm信息

sysenv

查看当前JVM的环境属性(System Environment Variables)
sysenv
查看单个

sysenv JAVA_HOME

quit/exit

使用quit或exit会退出arthas操作界面,但arthas进程还在,这时当你想监听别的java进程时会报错。因为arthas默认使用的是3658端口,所以这时会报错3658端口被别的进程占用。
退出
使用shutdown或stop可以结束arthas,结束后可以监听别的进程id

出现端口被占用报错时,可以再次启动进入刚才监听的进程或者使用telnet进入刚才监听的arthas进程界面,执行shutdown后stop结束监听

telnet localhost 3658 

卸载

linu系统中执行如下命令进行卸载

rm -rf ~/.arthas/ 
rm -rf ~/logs/arthas 

window下需要进入C盘当前用户的目录,找到.arthas目录和logs/arthas目录进行删除

二、JVM参数使用

JVM介绍

1.JVM区域

JVM区域总体分两类,heap区和非heap区。
heap区又分:Eden Space(伊甸园)、Survivor Space(幸存者区)、Tenured Gen(老年代-养老区)。
非heap区又分:Code Cache(代码缓存区)、Perm Gen(永久代)、Jvm Stack(java虚拟机栈)、Local Method Statck(本地方法栈)。

2.HotSpot虚拟机GC算法采用分代收集算法:

(1)一个人(对象)出来(new 出来)后会在Eden Space(伊甸园)无忧无虑的生活,直到GC到来打破了他们平静的生活。GC会逐一问清楚每个对象的情况,有没有钱(此对象的引用)啊,因为GC想赚钱呀,有钱的才可以敲诈嘛。然后富人就会进入Survivor Space(幸存者区),穷人的就直接kill掉。

(2)并不是进入Survivor Space(幸存者区)后就保证人身是安全的,但至少可以活段时间。GC会定期(可以自定义)会对这些人进行敲诈,亿万富翁每次都给钱,GC很满意,就让其进入了Genured Gen(养老区)。万元户经不住几次敲诈就没钱了,GC看没有啥价值啦,就直接kill掉了。

(3)进入到养老区的人基本就可以保证人身安全啦,但是亿万富豪有的也会挥霍成穷光蛋,只要钱没了,GC还是kill掉。

分区的目的:新生区由于对象产生的比较多并且大都是朝生夕灭的,所以直接采用标记-清理算法。而养老区生命力很强,则采用复制算法,针对不同情况使用不同算法。

非heap区域中Perm Gen中放着类、方法的定义,jvm Stack区域放着方法参数、局域变量等的引用,方法执行顺序按照栈的先入后出方式

JVM参数调优

1.默认JVM参数

未设置JVM参数

确保堆内存占用都不超过80%,即 heap(堆内存)、ps_eden_space(伊甸园区)、ps_survivor_space(幸存者区)、ps_old_gen(老年代)的使用都不超过80%

2.JVM参数:-Xms3g -Xmx3g -Xss1024k -XX:NewRatio=1 -XX:MetaspaceSize=128m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC

JVM参数设置

3.JVM 参数解析

(1)堆大小设置
JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G~2G;64为操作系统对内存无限制。我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m。
典型设置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k
-Xmx3550m	设置JVM最大可用内存为3550M。
-Xms3550m	设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g	设置年轻代大小为2G。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k	设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4	设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4	设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m	设置持久代大小为16m。
-XX:MaxTenuringThreshold=0	设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

(2)回收器选择
JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。

  • 1) 吞吐量优先的并行收集器
    如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
    典型配置:
java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20
-XX:+UseParallelGC	选择垃圾收集器为并行收集器。此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。
-XX:ParallelGCThreads=20	配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC
-XX:+UseParallelOldGC	配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100
-XX:MaxGCPauseMillis=100	设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100-XX:+UseAdaptiveSizePolicy
-XX:+UseAdaptiveSizePolicy	设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

(3)响应时间优先的并发收集器
如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
典型配置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC
-XX:+UseConcMarkSweepGC	设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。
-XX:+UseParNewGC	设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction	由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。
-XX:+UseCMSCompactAtFullCollection	打开对年老代的压缩。可能会影响性能,但是可以消除碎片

常见配置汇总

堆设置

  • -Xms:初始堆大小
  • -Xmx:最大堆大小
  • -XX:NewSize=n:设置年轻代大小
  • -XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
  • -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
  • -XX:MaxPermSize=n:设置持久代大小

收集器设置

  • -XX:+UseSerialGC:设置串行收集器
  • -XX:+UseParallelGC:设置并行收集器
  • -XX:+UseParalledlOldGC:设置并行年老代收集器
  • -XX:+UseConcMarkSweepGC:设置并发收集器

垃圾回收统计信息

  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintGCTimeStamps
  • -Xloggc:filename

并行收集器设置

  • -XX:ParallelGCThreads=n:设置并行收集器收集时使用的CPU数。并行收集线程数。
  • -XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间
  • -XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

并发收集器设置

  • -XX:+CMSIncrementalMode:设置为增量模式。适用于单CPU情况。
  • -XX:ParallelGCThreads=n:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数。并行收集线程数。

调优总结

年轻代大小选择

  • 响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
  • 吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。

年老代大小选择

  • 响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
    • 并发垃圾收集信息
    • 持久代并发收集次数
    • 传统GC信息
    • 花在年轻代和年老代回收上的时间比例
      减少年轻代和年老代花费的时间,一般会提高应用的效率
  • 吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。

较小堆引起的碎片问题

因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:

  • -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
  • -XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩

jvm内存与垃圾回收重点总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值