一, 内存过高
1, 内存过高一般有两种情况: 内存溢出和内存泄漏
(1) 内存溢出: 程序分配的内存超出物理机的内存大小, 导致无法继续分配内存, 出现 OOM 报错
(2) 内存泄漏: 不再使用的对象一直占据着内存不释放, 导致这块内存浪费掉, 久而久之, 内存泄漏的对象堆积起来, 也会导致物理机的内存被耗尽, 出现 OOM 报错
2, 内存过高的检测办法: 通常我们的 Java 服务器部署在 Linux 机器上面, 可以通过 jvm 自带的命令进行一些检测
(1) 查看对象的数目和占用内存大小
1参数为 Java 程序的进程号, 将结果导出到指定目录中,
jmap -histo:live < 进程号 >> < 导出目录 + 文件名 >
2示例如下, 可以看到程序中各个对象所占用内存的情况, 根据占用字节数大小降序显示, 这里只能看出哪些对象占用内存高, 但是还不能具体定位到问题代码, 需要进一步排查
3一些特殊的标识的含义
[C 表示 char[], 一般与 String 对象相关, 因为 String 其实就是基于 char 数组实现的
[S 表示 short[]
[I 表示 int[]
[B 表示 byte[]
[II 表示 int[][]
- num #instances #bytes class name
- ----------------------------------------------
- 1: 585152 75635896 [C
- 2: 66541 71446496 [B
- 3: 1141734 36535488 java.util.HashMap$Entry
- 4: 176622 26086840 <constMethodKlass>
- 5: 176622 24034208 <methodKlass>
- 6: 17717 19584560 <constantPoolKlass>
- 7: 174454 18375128 [Ljava.util.HashMap$Entry;
- 8: 571222 13709328 java.lang.String
- 9: 832783 13324528 java.lang.Integer
- 10: 17717 13198840 <instanceKlassKlass>
- 11: 15092 11237440 <constantPoolCacheKlass>
- 12: 46779 10429728 [I
- 13: 191501 7660040 java.util.LinkedHashMap$Entry
- 14: 12599 6567592 <methodDataKlass>
- 15: 113526 6357456 java.util.HashMap
- 16: 197998 6335936 java.util.Hashtable$Entry
(2) 如果需要进一步定位问题代码, 那么就需要把 Java 程序的内存镜像导出, 再具体分析了, 通过如下命令导出程序的内存镜像
jmap -dump:format=b,file=< 导出目录 + 文件名 > < 进程号 >
(3) 下载 Memory Analyzer 工具来分析内存镜像
http://www.eclipse.org/mat/
(4) 打开软件后, File-->Open Heap Dump..., 打开刚才导出的镜像文件, 选择 Leak Suspects Report,Finish, 进入分析页面
Histogram: 列表展示出内存中的对象数目和占用内存大小
Dominator Tree: 列表展示出程序中每个线程中的对象数目和占用内存大小
Top Consumers: 图表展示出每个线程的对象数目和占用内存大小
Top Components: 图表展示出内存中的对象数目和占用内存大小
Leak Suspects: 这个是最常用的, 会自动检测分析内存异常的原因
右键对象 -->show objects by class 可以查看对象的具体情况
by incomming reference: 显示引用该对象 / 线程的其他对象
by outgoing reference: 显示当前对象 / 线程引用的其他对象
(5) 点击 Leak Suspects, 程序会分析出可能存在内存问题的地方, 继续点击 Detail 可以看到具体有哪些对象和线程, 接下来就要根据具体情况具体分析了
二, CPU 过高
1, 当程序发现 CPU 过高的情况时, 可以使用 Windows 系统的 Process Explorer 工具来找到 CPU 高消耗的线程, 所以需要在 Windows 机器上面搭建好服务器的测试环境, 尽量模拟出线上 CPU 飙升的情况
2, 模拟好环境后, 通过任务管理器, 在进程一栏中找到 Java 程序的进程号
3, 下载 Process Explorer 工具
https://docs.microsoft.com/zh-cn/sysinternals/downloads/process-explorer
4, 打开工具后, 根据刚才的进程号找到进程
5, 然后右键 -->Properties, 再选择 Threads 选项卡, 点进 CPU 排序, 可以找到消耗 CPU 最多的那个线程
6, 使用科学计算器, 将十进制的线程号转成十六进制, 比如 493620-->78834
7, 到此已经拿到了可能出问题的进程号和线程号, 接下来使用 jvm 内置的命令来导出 Java 的堆栈信息
jstack -l < 进程号 >> < 导出目录 + 文件名 >
8, 打开导出的堆栈信息, 并全文搜索刚刚拿到的十六进制的线程号, 就可以找到出问题的代码具体位置了
来源: https://www.cnblogs.com/orange911/p/10414056.html