概览
上篇文章说到了Java内存模型(。。。),主要分为寄存器、堆内存、栈内存、常量池、代码段(在不同的虚拟机又叫方法区,或者永久代)、全局数据段。先复盘一下,遇到OOM时,我们应该怎么思考去解决这个问题:
- 首先,前置条件是清楚java虚拟机的内存模型,知道哪里都存储了什么;
- 其次,找到工具,能够帮助我们去看到每个存储区域都存储了多少东西,到底是哪个存储区域存储满了,因为存储了什么东西而导致内存不够使用;
- 最后,找到问题以后,就要去对应到代码上,然后解决问题。 第一步,我们在第一篇文章中已经大概讲解了,下面就针对第二步,介绍几种经常使用的工具,帮助我们去查看java进程的内存使用情况
系统指标查看
首先,熟用top命令,top是最基础的性能查看命令,能实时查看机器的cup、内存等的使用状态,以及每个进程对cpu、内存等的使用情况。当遇上OOM问题时,一般java进程会消耗掉系统的大部分内存资源,有些情形可能会同时消耗掉大量cpu资源。如果不是,就可能存在异常情况,比如启动java程序时,是否设置的最大内存比较小太小,从而导致OOM。java进程的启动参数是指最大堆内存(-Xmxn)、最大线程栈内存(-Xssn)、关闭针对class的gc功能(-Xnoclassgc)等,jinfo命令可以查看java进程的启动参数:
linux下可以尝试使用tsar这个性能监控工具,我的电脑是mac系统,还编译不出来tsar
java各个区域内存查看
- 内存的查看方式,大概可以分为两种:第一种是将OOM时,进程的内存以二进制文件的方式dump出来,然后事后分析;第二张时,使用工具,实时查看内存状态;内存的dump可以使用gcore和jmap,其中gcore是linux命令,可以对所有的进程dump,jmap是java自带命令。gcore命令速度比jmap速度快,当内存很大时,建议使用gcore;dump出来的二进制文件,可以使用jhat命令分析。后面主要讲的是实时查看内存情况,主要工具是btrace和java自带的一些命令,btrace作用是跟踪,而java命令是可以直接查看内存;
- 寄存器,主要存放的是程序计数器等,一般不会引发OOM;
- 堆内存,是引发OOM的重灾区,如果想查看堆内存的使用状况,可以使用jmap -heap ,或者jstat ,这里我不能仔细的讲解每一个命令,因为想完全弄明白任何一条命令,都需要另外一篇文章来介绍。如果知道可能是哪里的问题,可以使用btrace根据代码,确认某段代码在执行的过程中对内存的消耗和使用;
- 栈内存,也会引发OOM,当系统中线程太多,导致线程栈内存总量超过最大栈内存时,或者创建一个新线程失败时,就会出现OOM。当然,每个线程栈的大小都会受到单线程最大栈内存的限制,当单线程内存超过这个阈值时,会报stackOverFlow。想看栈内存,每个线程栈的状态,可以使用jstack命令;
- 永久代,主要存放的是类信息,如果永久代满了活着超过了临界值,会出发完全垃圾回收(full gc)。如果仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小堆避免full gc是非常重要的原因。jmap -permstat 命令可以查看永久代的内存使用状态。
- 注:java 8中已经去除了永久代,换成meta space,可以使用jmap -clstats 查看所有类的加载信息。在java 8 中,使用jstat命令,内存的状态信息中可以看到meta space的使用情况; *常量池和全局数据段,一般不会引发OOM。
——————-我是微信公众号分割线—————-