前言

这段时间我在做一个个人的微信小程序项目,后端使用 Java 开发,服务器购买的是阿里云的 2 核 4G 的服务器,对于 Java 这种比较吃内存的应用来说,这点配置还是比较局促的

今天上服务器的时候发现内存占用到了 3G,通过 htop 命令查看内存的时候,发现就是 Java 应用占了大头

于是准备决定用 Arthas 分析一下内存占用情况

安装

通过官网链接可以知道,Arthas 其实只需要下载之后直接运行 Jar 包即可

https://arthas.aliyun.com/doc/install-detail.html
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 进程的执行用户,和当前用户不一致,导致的权限问题

https://github.com/alibaba/arthas/issues/2627

确实没错,我的这个业务进程使用的是 www 用户执行,而当前终端内,运行 Arthas 的用户是 root

所以只需要将执行业务进程的用户修改成 root 即可

生产环境中不建议直接这么做,建议通过合适的用户来执行相应的操作

结果

699bb95e45bf2_1771813214.png

最后是成功连接上了 JVM,能够通dashboard查看到信息

通过使memory命令,能够查看到各个年龄代的内存占用情况

可以看到,大部分对象是在 Eden 区,占用了 1G 多,我手动请求了几次后端,内存增加之后,就触发了 Young GC,对 Eden 区进行了清理

不过由于我设置使用了 G1GC,所以分代只是逻辑上的,没有在物理模型中做限制,毕竟 G1GC 是 Region 的内存模型,所以memory命令上可以看到max-1