1.OOM
1.1 常见原因
>> 内存分配确实过小
>> 频繁创建对象,没有及时释放
>> 频繁申请系统资源,导致系统资源耗尽(例如:不断创建线程,不断发起网络连接)
1.2 定位与解决方法
#需要先找到出问题的进程,使用top命令定位:
输入top命令后,可以按P(shift+p)根据cpu占用排序、按M根据内存占用排序、按T根据运行时间排序。
(可以先按c显示具体的command)
#这里先按M根据内存排序查找异常的进程:
这里假设出现异常的进程pid为28613
#注意
定位问题前请先尝试输入jps命令,确定是否能够显示出现问题的pid(例如28613).
如果jps没有相应的显示,可能是你当前用户的权限不够,
请使用启用相应进程的用户或者拥有更高权限的用户排查问题!
不然以下的一些命令(例如jmap)将无法使用.
#原理
java程序启动后,默认会在/tmp/hsperfdata_userName目录下以该进程的id为文件名新建文件,
并在该文件中存储jvm运行的相关信息,其中的userName为当前的用户名,
/tmp/hsperfdata_userName目录会存放该用户所有已经启动的java进程信息.
而jps、jconsole等工具的数据来源就是这个文件(/tmp/hsperfdata_userName/pid)。
所以当该文件不存在或是无法读取时就会出现jps无法查看该进程号,jconsole无法监控等问题
1.2.1 判断是否是由于“内存分配确实过小”
#输入以下命令, 可以看到详细的堆内存使用情况,可以以此判断应用分配的内存是否确实过小
jmap -heap 28613
1.2.2 判断是否是由于“频繁创建对象,没有及时回收”
#输入以下命令,找出最耗内存的对象
jmap -histo:live 28613 | more
#输入命令后,会以表格的形式显示存活对象的信息,并按照所占内存大小排序
[root@bogon~]# jmap -histo:live 2879 | more
num #instances #bytes class name
----------------------------------------------
1: 81515 11259264 [C
2: 80010 1920240 java.lang.String
3: 44265 1416480 java.util.concurrent.ConcurrentHashMap$Node
4: 4057 1369872 [B
5: 11200 1241976 java.lang.Class
6: 8977 789976 java.lang.reflect.Method
7: 18801 752040 java.util.LinkedHashMap$Entry
8: 5144 627336 [I
9: 11948 601184 [Ljava.lang.Object;
10: 8275 595352 [Ljava.util.HashMap$Node;
11: 18231 583392 java.lang.ref.WeakReference
12: 9847 551432 java.util.LinkedHashMap
13: 375 544288 [Ljava.util.concurrent.ConcurrentHashMap$Node;
14: 29118 465888 java.lang.Object
15: 13034 417088 java.util.HashMap$Node
16: 8850 354000 java.lang.ref.SoftReference
17: 10490 251760 java.beans.MethodRef
18: 3586 200816 java.beans.MethodDescriptor
19: 6450 195288 [Ljava.lang.String;
>> instances: 对象实例数量
>> bytes: 占用内存大小
>> class name: 类名
可以看到目前最耗内存的对象也才占用内存11m,所以属于正常范畴
如果发现某个对象的占用大量内存(例如:1G以上),就需要review代码,审查下该对象是否没有及时回收
jmap -heap输出的非自定义类名说明.-->最后一列class name 的解释说明png
1.2.3 判断是否是由于“频繁申请系统资源”
#查看进程的线程数量:
ll /proc/{PID}/task | wc -l
>> 执行命令
[root@bogon ~]# ll /proc/28613/task | wc -l
101
>> 可以看到,该进程使用了101个线程。
>> 如果看到启用了大量线程,就需要审查代码涉及到线程池的使用部分,是否限定了最大线程数量。
#查询进程占用的句柄数量
ll /proc/{PID}/fd | wc -l
>> 执行命令
[root@bogon~]# ll /proc/28613/fd | wc -l
38
>> 可以看到,该进程使用了38个文件句柄。
>> 如果占用大量文件句柄,需要审查代码中涉及到文件操作和网络连接操作是否有及时关闭资源链接。