前言
这段时间我在做一个个人的微信小程序项目,后端使用 Java 开发,服务器购买的是阿里云的 2 核 4G 的服务器,对于 Java 这种比较吃内存的应用来说,这点配置还是比较局促的
今天上服务器的时候发现内存占用到了 3G,通过 htop 命令查看内存的时候,发现就是 Java 应用占了大头
于是准备决定用 Arthas 分析一下内存占用情况
安装
通过官网链接可以知道,Arthas 其实只需要下载之后直接运行 Jar 包即可
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar遇到的问题
在一开始使用的时候,就遇到了问题,Arthas 在连接到目前服务器上的 JVM 进程时失败,报错
root@server:~# java -jar arthas-boot.jar
[INFO] JAVA_HOME: /opt/java/jdk-21.0.10+7
[INFO] arthas-boot version: 4.1.5
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 455221 /opt/app/my_project/MyProject-0.0.1-SNAPSHOT.jar
1
[INFO] arthas home: /root/.arthas/lib/4.1.5/arthas
[INFO] Try to attach process 455221
Picked up JAVA_TOOL_OPTIONS:
[ERROR] Start arthas failed, exception stack trace:
com.sun.tools.attach.AgentLoadException: Agent JAR not found or no Agent-Class attribute
at jdk.attach/sun.tools.attach.HotSpotVirtualMachine.loadAgent(HotSpotVirtualMachine.java:172)
at com.taobao.arthas.core.Arthas.attachAgent(Arthas.java:122)
at com.taobao.arthas.core.Arthas.<init>(Arthas.java:27)
at com.taobao.arthas.core.Arthas.main(Arthas.java:161)
[INFO] Attach process 455221 success.
[INFO] arthas-client connect 127.0.0.1 3658
Connect to telnet server error: 127.0.0.1 3658
java.net.ConnectException: Connection refused
at java.base/sun.nio.ch.Net.pollConnect(Native Method)
at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:694)
at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:542)
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:592)
at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
at java.base/java.net.Socket.connect(Socket.java:751)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:188)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:209)
at com.taobao.arthas.client.TelnetConsole.process(TelnetConsole.java:306)
at com.taobao.arthas.client.TelnetConsole.main(TelnetConsole.java:166)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at com.taobao.arthas.boot.Bootstrap.main(Bootstrap.java:634)
Usage: arthas-client [--help] [-c <value>] [-f <value>] [-w <value>] [-t
<value>] [-h <value>] [target-ip] [port]
Arthas Telnet Client
EXAMPLES:
java -jar arthas-client.jar 127.0.0.1 3658
java -jar arthas-client.jar -c 'dashboard -n 1'
java -jar arthas-client.jar -f batch.as 127.0.0.1
Options and Arguments:
--help Print usage
-c,--command <value> Command to execute, multiple commands
separated by ;
-f,--batch-file <value> The batch file to execute
-w,--width <value> The terminal width
-t,--execution-timeout <value> The timeout (ms) of execute commands or batch
file
-h,--height <value> The terminal height
<target-ip> Target ip
<port> The remote server port通过上网查阅后发现,原因很有可能是因为需要连接的目标 JVM 进程的执行用户,和当前用户不一致,导致的权限问题
确实没错,我的这个业务进程使用的是 www 用户执行,而当前终端内,运行 Arthas 的用户是 root
所以只需要将执行业务进程的用户修改成 root 即可
生产环境中不建议直接这么做,建议通过合适的用户来执行相应的操作
结果

最后是成功连接上了 JVM,能够通dashboard查看到信息
通过使memory命令,能够查看到各个年龄代的内存占用情况

可以看到,大部分对象是在 Eden 区,占用了 1G 多,我手动请求了几次后端,内存增加之后,就触发了 Young GC,对 Eden 区进行了清理
不过由于我设置使用了 G1GC,所以分代只是逻辑上的,没有在物理模型中做限制,毕竟 G1GC 是 Region 的内存模型,所以memory命令上可以看到max 是 -1