JDK自带的工具
在JDK的bin目录下有很多命令行工具:
我们可以看到各个工具的大小基本上都稳定在27kb左右,这个不是JDK开发团队刻意为之的,而是因为这些工具大多数是jdk\lib\tools.jar类库的一层薄包装而已,他们的主要功能代码是在tools类库中实现的。命令行工具的好处是:当应用程序部署到生产环境后,无论是直接接触物理服务器还是远程telnet到服务器上都会受到限制。而借助tools.jar类库里面的接口,我们可以直接在应用程序中实现功能强大的监控分析功能。
常用命令:
这里主要介绍如下几个工具:
1、jps:查看本机java进程信息
2、jstack:打印线程的栈信息,制作 线程dump文件
3、jmap:打印内存映射信息,制作 堆dump文件
4、jstat:性能监控工具
5、jhat:内存分析工具,用于解析堆dump文件并以适合人阅读的方式展示出来
6、jconsole:简易的JVM可视化工具
7、jvisualvm:功能更强大的JVM可视化工具
8、javap:查看字节码
JAVA Dump:
JAVA Dump就是虚拟机运行时的快照,将虚拟机运行时的状态和信息保存到文件中,包括:
线程dump:包含所有线程的运行状态,纯文本格式
堆dump:包含所有堆对象的状态,二进制格式
1、jps
显示当前所有java进程pid的命令,我们可以通过这个命令来查看到底启动了几个java进程(因为每一个java程序都会独占一个java虚拟机实例),不过jps有个缺点是只能显示当前用户的进程id,要显示其他用户的还只能用linux的ps命令。
执行jps命令,会列出所有正在运行的java进程,其中jps命令也是一个java程序。前面的数字就是进程的id,这个id的作用非常大,后面会有相关介绍。
jps -help:
jps -l 输出应用程序main.class的完整package名或者应用程序jar文件完整路径名
jps -v 输出传递给JVM的参数
jps失效
我们在定位问题过程会遇到这样一种情况,用jps查看不到进程id,用ps -ef | grep java却能看到启动的java进程。
要解释这种现象,先来了解下jps的实现机制:
java程序启动后,会在目录/tmp/hsperfdata_{userName}/下生成几个文件,文件名就是java进程的pid,因此jps列出进程id就是把这个目录下的文件名列一下而已,至于系统参数,则是读取文件中的内容。
我们来思考下:如果由于磁盘满了,无法创建这些文件,或者用户对这些文件没有读的权限。又或者因为某种原因这些文件或者目录被清除,出现以上这些情况,就会导致jps命令失效。
如果jps命令失效,而我们又要获取pid,还可以使用以下两种方法:
1、top | grep java
2、ps -ef |grep java
2、jstack
主要用于生成指定进程当前时刻的线程快照,线程快照是当前java虚拟机每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致长时间等待。
3、jmap
主要用于打印指定java进程的共享对象内存映射或堆内存细节。
堆Dump是反映堆使用情况的内存镜像,其中主要包括系统信息、虚拟机属性、完整的线程Dump、所有类和对象的状态等。一般在内存不足,GC异常等情况下,我们会去怀疑内存泄漏,这个时候就会去打印堆Dump。
jmap的用法摘要:
1、jmap pid
打印的信息分别为:共享对象的起始地址、映射大小、共享对象路径的全程。
2、jmap -heap pid:查看堆使用情况
3、jmap -histo pid:查看堆中对象数量和大小
打印的信息分别是:序列号、对象的数量、这些对象的内存占用大小、这些对象所属的类的全限定名
如果是内部类,类名的开头会加上*,如果加上live子参数的话,如jmap -histo:live pid,这个命名会触发一次FUll GC,只统计存活对象
4、jmap -dump:format=b,file=heapdump pid:将内存使用的详细情况输出到文件
然后使用jhat命令查看该文件:jhat -port 4000 文件名 ,在浏览器中访问http:localhost:4000/
总结:
该命令适用的场景是程序内存不足或者GC频繁,这时候很可能是内存泄漏。通过用以上命令查看堆使用情况、大量对象被持续引用等情况。
4、jstat
主要是对java应用程序的资源和性能进行实时的命令行监控,包括了对heap size和垃圾回收状况的监控。
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
option:我们经常使用的选项有gc、gcutil
vmid:java进程id
interval:间隔时间,单位为毫秒
count:打印次数
1、jstat -gc PID 5000 20
S0C:年轻代第一个survivor的容量(字节)
S1C:年轻代第二个survivor的容量(字节)
S0U:年轻代第一个survivor已使用的容量(字节)
S1U:年轻代第二个survivor已使用的容量(字节)
EC:年轻代中Eden的空间(字节)
EU:年代代中Eden已使用的空间(字节)
OC:老年代的容量(字节)
OU:老年代中已使用的空间(字节)
PC:永久代的容量
PU:永久代已使用的容量
YGC:从应用程序启动到采样时年轻代中GC的次数
YGCT:从应用程序启动到采样时年轻代中GC所使用的时间(单位:S)
FGC:从应用程序启动到采样时老年代中GC(FULL GC)的次数
FGCT:从应用程序启动到采样时老年代中GC所使用的时间(单位:S)
2、jstat -gcutil PID 5000 20
s0:年轻代中第一个survivor已使用的占当前容量百分比
s1:年轻代中第二个survivor已使用的占当前容量百分比
E:年轻代中Eden已使用的占当前容量百分比
O:老年代中已使用的占当前容量百分比
P:永久代中已使用的占当前容量百分比
5、jhat
主要用来解析java堆dump并启动一个web服务器,然后就可以在浏览器中查看堆的dump文件了。
生成dump文件的方法前面已经介绍了,这边主要介绍如何解析java堆转储文件,并启动一个web server
jhat heapdump
这个命令将heapdump文件转换成html格式,并且启动一个http服务,默认端口为7000。
如果端口冲突,可以使用以下命令指定端口:jhat -port 4000 heapdump
下面我们来访问下:ip:port
6、jinfo
jinfo可以用来查看正在运行的java运用程序的扩展参数,甚至支持在运行时动态地更改部分参数。
基本使用语法如下: jinfo -< option > < pid > ,其中option可以为以下信息:
-flag< name >: 打印指定java虚拟机的参数值
-flag [+|-]< name >:设置或取消指定java虚拟机参数的布尔值
-flag < name >=< value >:设置指定java虚拟机的参数的值
使用示例
下面的命令显示了新生代对象晋升到老年代对象的最大年龄。在运行程序运行时并没有指定这个参数,但是通过jinfo,可以查看这个参数的当前的值。
下面的命令显示是否打印gc详细信息:
下面的命令在运用程序运行时动态打开打印详细gc信息开关:
注意事项:jinfo虽然可以在java程序运行时动态地修改虚拟机参数,但并不是所有的参数都支持动态修改。
7、jcmd
在JDK 1.7之后,新增了一个命令行工具jcmd。它是一个多功能工具,可以用来导出堆,查看java进程,导出线程信息,执行GC等。jcmd拥有jmap的大部分功能,Oracle官方建议使用jcmd代替jmap。
使用 jcmd -l 命令列出当前运行的所有虚拟机,示例:
针对每一个虚拟机,可以使用help命令列出该虚拟机支持的所有命令,示例:
子命令含义:
- VM.native_memory
- VM.commercial_features
- GC.rotate_log
- ManagementAgent.stop
- ManagementAgent.start_local
- ManagementAgent.start
- Thread.print, 打印线程栈信息
- GC.class_histogram, 查看系统中类统计信息
- GC.heap_dump, 导出堆信息,与jmap -dump功能一样
- GC.run_finalization, 触发finalize()
- GC.run, 触发gc()
- VM.uptime, VM启动时间
- VM.flags, 获取JVM启动参数
- VM.system_properties, 获取系统Properties
- VM.command_line, 启动时命令行指定的参数
- VM.version
- help
示例:
8、可视化监控工具(JConsole、JVisualVM)
集上面之大成,并提供了可视化的界面;还可以监控远程Java服务;支持监控JMX。
JVisualVM比JConsole更强大:支持对CPU、内存运行进行采样、配置。推荐用JVisualVM。
JConsole监控页面示例:
JVisualVM监控页面示例:
参考资料
http://www.cnblogs.com/dongguacai/p/5910134.html
http://qifuguang.me/categories/%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/
其他工具
JOL(即Java Object Layout):OpenJDK提供的库,用于查看Java对象的内存布局,这个很有用,可以借助它来跟踪锁升级等过程。只需要引入Maven即可使用,示例:
//引入依赖
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.16</version>
</dependency>
//代码
class TTTT {
public static void main(String[] args) {
System.err.println(ClassLayout.parseInstance(new Person()).toPrintable());
System.err.println(ClassLayout.parseClass(Person.class).toPrintable());
}
}
class Person {
private int age = 1;
private String name = "zhangsan";
}
//代码执行结果
com.marchon.learning.Person object internals:
OFF SZ TYPE DESCRIPTION VALUE
0 8 (object header: mark) 0x0000005e4c804101 (hash: 0x5e4c8041; age: 0)
8 4 (object header: class) 0xf8010dd9
12 4 int Person.age 1
16 4 java.lang.String Person.name (object)
20 4 (object alignment gap)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
openJDK源码:查看 JDK native 方法的实现
strace:跟踪程序运行过程发起的系统调用
https://fastthread.io:线程栈分析的网站
详情参阅文章 看见Java-公众号低并发编程。
线上问题排查思路(八股)
硬盘使用情况:du 命令
内存使用且情况:free 命令
CPU使用情况:top 命令
网络使用情况:netstat 命令
磁盘空间不足问题
先用 df -h 从总体查看磁盘状态
文件系统 容量 已用 可用 已用% 挂载点
devtmpfs 1.9G 4.0K 1.9G 1% /dev
tmpfs 1.9G 24K 1.9G 1% /dev/shm
tmpfs 1.9G 740K 1.9G 1% /run
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/vda1 50G 12G 36G 25% /
tmpfs 379M 0 379M 0% /run/user/0
tmpfs 379M 0 379M 0% /run/user/1001
/dev/loop4 1003K 17K 935K 2% /mnt
很多都是无用信息,比如 tmpfs 类型的是 linux 的内存型文件系统,里面的数据是放在内存中的,你可 cd 到后面那些目录下玩玩,和操作正常的文件是一样的。
一般就是看挂载点为根目录的 / 的容量咯,这里我只用了 25%,显然还没有达到瓶颈,但如果这里太大了,还要进一步看看是哪个目录大了。
此时用 du -sh * 命令,查看 / 路径下的各个文件和目录的大小。
0 bin
166M boot
134M data
4.0K dev
40M etc
4.0K home
0 lib
0 lib64
16K lost+found
4.0K media
du: 无法访问"mnt/hello": 输入/输出错误
13K mnt
20K opt
du: 无法访问"proc/12896/task/12896/fd/4": 没有那个文件或目录
du: 无法访问"proc/12896/task/12896/fdinfo/4": 没有那个文件或目录
du: 无法访问"proc/12896/fd/4": 没有那个文件或目录
du: 无法访问"proc/12896/fdinfo/4": 没有那个文件或目录
0 proc
953M root
740K run
0 sbin
4.0K srv
2.1G swapfile
0 sys
68K tmp
5.3G usr
2.9G var
找到最大的那个目录,进去,再次执行这个命令,直到找到最终占地面积特别大的文件或目录为止。
当然,如果你知道里面全都是普通文件,比如你发现你的 log 目录很大,想看看里面哪个具体日志文件过大,也可以用 ls -lh 命令,它的输出会更丰满一些,但我们主要看占地大小,也就无所谓了。
drwxr-x--- 2 www www 4.0K 2 月 3 14:56 hsperfdata_www
srwxrwxrwx 1 mysql mysql 0 2 月 3 14:56 mysql.sock
-rw-r--r-- 1 root root 0 6 月 19 15:33 stargate.lock
drwx------ 3 root root 4.0K 2 月 3 14:55 systemd-private
另外提一嘴,你如果用 du -h * 去执行的话,整个屏幕会很刺激哟,你可以试试,哈哈~
CPU 与内存使用率过高问题
到了我临场发挥时,唯一能记得住的命令了,那就是 top。
top - 20:16:56 up 141 days, 5:21, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 110 total, 1 running, 109 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 0.5 sy, 0.0 ni, 99.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3880160 total, 135320 free, 852980 used, 2891860 buff/cache
KiB Swap: 2097148 total, 2087676 free, 9472 used. 2737524 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11866 root 20 0 611652 14280 2396 S 0.7 0.4 239:17.79 barad_agent
16850 root 20 0 1026344 22364 11644 S 0.7 0.6 5:04.40 YDService
1550 root 20 0 140996 2428 1472 S 0.3 0.1 58:38.76 redis-server
1575 www 20 0 3610540 358456 14912 S 0.3 9.2 153:55.63 java
16868 root 20 0 773168 12576 11056 S 0.3 0.3 1:15.83 YDEdr
下面的列表表示不同进程(PID)所占用的资源情况,没错,不仅仅是 CPU 情况。
VIRT 表示使用的虚拟内存数量,RES 表示使用的物理内存数量,SHR 表示使用的共享内存数量,这三者可以从内存角度看该进程的资源占用情况。
S 表示进程的状态,下面的值 S 表示睡眠,D 表示不可中断睡眠,R 表示运行,基本知道这三个就够了。
后面两个值是百分比,%CPU 自然就是 CPU 使用率,%MEM 自然就是内存使用率,看这俩值可以一目了然看谁占用的资源过高了。
TIME 表示累计 cpu 使用时长,感觉没什么用。
COMMAND 表示启动进程使用的命令行,Java 程序的话,可以看看 JVM 启动参数,看是否配置的合理。
如果专门看 Java 进程的情况,可以先 jps 命令找到它的 PID。
19063 jar
1575 Bootstrap
21263 Jps
然后再 top -p 19063 专门看这个 Java 进程的情况。
top - 20:51:07 up 141 days, 5:55, 3 users, load average: 0.09, 0.
Tasks: 1 total, 0 running, 1 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.7 us, 0.3 sy, 0.0 ni, 99.0 id, 0.0 wa, 0.0 hi, 0.0
KiB Mem : 3880160 total, 139956 free, 952180 used, 2788024 buf
KiB Swap: 2097148 total, 2087420 free, 9728 used. 2638260 ava
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+
19063 root 20 0 3533736 101916 13012 S 0.3 2.6 0:04.31
如果再细化到线程,可以加个 -H 参数,top -p 19063 -H
top - 20:52:16 up 141 days, 5:56, 3 users, load average: 0.03, 0.
Threads: 22 total, 0 running, 22 sleeping, 0 stopped, 0 zomb
%Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0
KiB Mem : 3880160 total, 140136 free, 951924 used, 2788100 buf
KiB Swap: 2097148 total, 2087420 free, 9728 used. 2638512 ava
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+
19063 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.00
19064 root 20 0 3533736 101920 13012 S 0.0 2.6 0:01.16
19065 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.01
19066 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.01
19067 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.02
19068 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.00
19069 root 20 0 3533736 101920 13012 S 0.0 2.6 0:00.00
当然,top 命令已经可以分析内存了,如果想单独分析下内存,可以用小而美的命令,free -h
total used free shared buff/cache available
Mem: 3.7G 934M 155M 500K 2.6G 2.5G
Swap: 2.0G 9.5M 2.0G
这些参数的含义是:
total:内存总数
used:已经使用内存数
free:完全空闲内存
shared:多个进程共享的内存
buffers:用于块设备数据缓冲,记录文件系统 metadata(目录,权限,属性等)
cached:用于文件内容的缓冲
available:真正剩余的可被程序应用的内存数
一共有两行,mem 和 swap,mem 就是内存大小,swap 是交换区,是在物理磁盘上的一块区域,当内存不够用时,可以用这部分区域当内存。
可以用 swapon 命令来看下交换区的使用情况。
NAME TYPE SIZE USED PRIO
/swapfile file 2G 9.5M -2
网络延迟
netstat -a 查看所有连接中的 socket。
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:mysql 0.0.0.0:* LISTEN
tcp 0 0 VM-0-12-centos:6379 0.0.0.0:* LISTEN
tcp 0 0 VM-0-12-centos:memcache 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:http 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ftp 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:ssh 0.0.0.0:* LISTEN
tcp 0 0 VM-0-12-centos:smtp 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:irdmi 0.0.0.0:* LISTEN
tcp 0 0 VM-0-12-centos:12419 169.254.0.4:http TIME_WAIT
tcp 0 0 VM-0-12-centos:ssh 114.247.175.201:8829 ESTABLISHED
tcp 0 0 VM-0-12-centos:64390 169.254.0.55:lsi-bobcat ESTABLISHED
tcp 0 0 VM-0-12-centos:ssh 114.247.175.201:14227 ESTABLISHED
tcp 0 0 VM-0-12-centos:ssh 114.247.175.201:22704 ESTABLISHED
tcp 0 36 VM-0-12-centos:ssh 114.247.175.201:15126 ESTABLISHED
tcp6 0 0 [::]:webcache [::]:* LISTEN
tcp6 0 0 [::]:ftp [::]:* LISTEN
tcp6 0 0 VM-0-12-centos:smtp [::]:* LISTEN
tcp6 0 0 [::]:irdmi [::]:* LISTEN
tcp6 0 0 VM-0-12-centos:8006 [::]:* LISTEN
udp 0 0 0.0.0.0:bootpc 0.0.0.0:*
udp 0 0 VM-0-12-centos:ntp 0.0.0.0:*
udp 0 0 VM-0-12-centos:ntp 0.0.0.0:*
udp6 0 0 VM-0-12-centos:ntp [::]:*
udp6 0 0 VM-0-12-centos:ntp [::]:*
用 netstat -tnpa 命令可以查看所有 tcp 连接的信息,包括进程号。
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:3306 0.0.0.0:* LISTEN 2903/mysqld
tcp 0 0 127.0.0.1:6379 0.0.0.0:* LISTEN 32647/redis-server
tcp 0 0 127.0.0.1:11211 0.0.0.0:* LISTEN 1240/memcached
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1030/nginx: master
tcp 0 0 0.0.0.0:21 0.0.0.0:* LISTEN 1016/pure-ftpd
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1201/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1502/master
tcp 0 0 0.0.0.0:8000 0.0.0.0:* LISTEN 1550/./redis-server
tcp 0 0 172.21.0.12:22 114.247.175.201:8829 ESTABLISHED 18961/sshd: root@no
tcp 0 0 172.21.0.12:64390 169.254.0.55:5574 ESTABLISHED 16850/YDService
tcp 0 0 172.21.0.12:22 114.247.175.201:14227 ESTABLISHED 9490/sshd: root@not
tcp 0 0 172.21.0.12:22 114.247.175.201:22704 ESTABLISHED 20259/sshd: root@no
tcp 0 36 172.21.0.12:22 114.247.175.201:15126 ESTABLISHED 9478/sshd: root@pts
tcp6 0 0 :::8080 :::* LISTEN 1575/java
tcp6 0 0 :::21 :::* LISTEN 1016/pure-ftpd
tcp6 0 0 ::1:25 :::* LISTEN 1502/master
tcp6 0 0 :::8000 :::* LISTEN 1550/./redis-server
tcp6 0 0 127.0.0.1:8006 :::* LISTEN 1575/java
得到进程号后就好说了配合 top 命令,ps -ef 命令,查看相关进程信息。
不过这些自带的网络命令,都不太灵活,一般我们有直观查看实时流量,然后进行一波统计分析的需求,这里介绍一个酷酷的命令。
输入 iftop -P 会得到这样的一个实时数据。
中间的 <= => 这两个左右箭头,表示的是流量的方向。
TX:发送流量
RX:接收流量
TOTAL:总流量
Cumm:运行 iftop 到目前时间的总流量
peak:流量峰值
rates:分别表示过去 2s 10s 40s 的平均流量
当然这些是从机器上的,如果是一个接口响应耗时过长,一方面可能是由于机器本身所在的网络有问题。
一般这种问题也就是运维同学去解决,或者我们单节点重启一下,换台机器,就搞定了。
还有可能是服务提供方业务耗时严重,这个就需要去排查服务提供方的日志,机器负载,连接池占用情况等,分析问题,这也是我们平时开发碰到的主要问题。
Java 程序的问题分析
这个也算是个八股文吧,平时生产环境中很少会用到,不过我倒是碰到过一些,但都很简单,主要借着故事来方便记忆一下具体命令吧。
有次我们的一个服务,内存是这样的,直到触发了报警。
用 jmap -dump 分析堆内存中的快照,未发现有大对象问题。
用 jmap -heap 查看堆内存设置与当前使用情况,堆内存设置的是 6G。
Attaching to process ID 127, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.202-b08
using thread-local object allocation.
Parallel GC with 4 thread(s)
Heap Configuration:
MinHeapFreeRatio = 0
MaxHeapFreeRatio = 100
MaxHeapSize = 6442450944 (6144.0MB)
NewSize = 2147483648 (2048.0MB)
MaxNewSize = 2147483648 (2048.0MB)
OldSize = 4294967296 (4096.0MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 21807104 (20.796875MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 17592186044415 MB
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
PS Young Generation
Eden Space:
capacity = 2117074944 (2019.0MB)
used = 1150960080 (1097.6410675048828MB)
free = 966114864 (921.3589324951172MB)
54.36558036180698% used
From Space:
capacity = 15204352 (14.5MB)
used = 13860864 (13.21875MB)
free = 1343488 (1.28125MB)
91.16379310344827% used
To Space:
capacity = 15204352 (14.5MB)
used = 0 (0.0MB)
free = 15204352 (14.5MB)
0.0% used
PS Old Generation
capacity = 4294967296 (4096.0MB)
used = 188289400 (179.56676483154297MB)
free = 4106677896 (3916.433235168457MB)
4.383954219520092% used
23604 interned Strings occupying 2341024 bytes.
用 jstack 查看 jvm 线程运行信息,上传到 fastthread.io 这个网站,直观地看一下,一看线程有点多。
一个线程需要占用大约 1M 的空间吧,而且不是占用 jvm 的内存空间,而是会占用操作系统空闲的内存空间。
我们的机器内存是 8G,堆内存占了 6G,线程数这么多快超过 2G 了,再加上操作系统里其他程序占用的内存,内存告警很正常,甚至可能 OOM。
所以,一方面我们可以减少线程数,另一方面可以把堆内存配置得小一点,使得堆内存加上线程占用的操作系统内存,不要超过 8G。
一些无法本地 debug 的调试技巧
有的时候线上忘记在关键节点打日志了,会导致一些问题,比如不知道方法入参的值,不知道某方法中具体某一步的耗时。
我一般用阿里的一款贼牛逼的工具 arthas 来排查。
先申请个线上机器的接近 root 的权限,然后把 arthas 工具下载到机器上,可以直接机器上下载:
wget https://alibaba.github.io/arthas/arthas-boot.jar
使用 watch 命令可以实时观察一个方法的入参和出参。
使用 trace 命令可以跟踪某个方法的耗时,而且可以深入这个方法所调用的方法的各个耗时。