JVM(五)–内存泄露与内存溢出
(包括一个想问面试题:怎么排查oom)
内存溢出:是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出
(比如一个桶装满了水,当放进一个苹果时桶的水正常,但是当你放进一个大石头,水就会溢出)。
==>解决方法:修改JVM启动参数(-Xms,-Xmx),直接增加内存
内存泄露:是你没有及时清理内存垃圾,导致系统无法再给你提供内存资源(内存资源耗尽)
==>也就是堆上分配的内存没有释放,从而失去对其控制。这样会造成程序能使用的内存越来越少,导致系统运行速度减慢,严重情况是使机器宕掉。
内存泄露就是存在一些被分配的对象,此对象有2种特征:
- ①对象是可达的,即在有向图中,存在通路可以与其相连
- ②对象是无用的,即程序不会再使用这些对象(
这些对象不会被GC回收,但是却占用内存。
)(也就是占着位置没有实用)
怎么排查下oom:
项目遇到最大的问题(OOM) :
排查过程:
分析oom 的原因: 主要分为内存泄漏和内存溢出:
内存泄漏:
对象分配了内存, 在方法调用结束之后没有进行回收,直接进入了老年代中
内存溢出:
我们的内存容量不够,导致内存分配不足
主要从这两方面进行排查:
首先排查的是内存溢出:我们机器的配置是 2核4g 的机器, 堆内存分配的是3G,按照1:2的比例进行分配;这里通过 jmap -heap 可以查看到我们的堆内存使用情况
.
然后根据 jstat -gc 查看我们的gc 次数, 可以粗略的查看到我们的系统gc 情况
当时通过分析 gc.log 文件看到fgc的次数相对来说还是比较少的
, 因此可以暂时排除我们内存溢出导致的oom 的可能性
.
其次就是排查内存泄漏了.这里使用到了 -XX:HeapDumpOnOutOfMemoryError 命令来保存 oom 时产生的堆栈信息
.
通过 MAT 工具来进行分析 内存使用情况
.
当时分析看到占用比较多内存的是 java.util.map 对象比较多. 通过 MAT 工具的 leak suspects 进行分析内存泄漏可能存在的原因
.
当时定位到的是我们的一个学生作业报告的接口的方法(这只是别人的分享例子).
然后查看了一下 这个接口的调用情况,发现一天的调用量在20万次左右,平均响应时间是在400毫秒.
根据分析到的有效信息, 初步排查就是由于这个接口调用量比较多,然后导致生成比较多的一些聚合数据(主要通过map 来进行聚合), 然后由于响应时间比较长,可能会导致在ygc 的时候,根据可达性分析(gc root)判断这个对象还是存活的,然后分配到了老年代,当方法调用结束了, 就会导致这部分对象会一直存活在老年代,直到触发fgc.
如果是正常情况下, 应该会在fgc 的时候就会触发垃圾回收, 而不是发生oom. 这里是根据查看我们ygc 产生的剩余对象占用内存来进行分析的, 即如果ygc 产生了大量的存活对象,而oldgc 没有足够的内存存放这部分对象,就会导致oom
.
优化过程:
1、jvm 的优化,主要有做了, 一个是增加内存,调整新生代和老年代的比例(修改成1:1),修改垃圾回收器
2、代码上面进行优化处理:
减少聚合数据对象的创建, 这个可以通过提前生成相应的报告数据
减少接口耗时
下面为一些常用命令:
1、jps:查看进程及其相关去信息
2、jmap:用来生成dump文件和查看堆相关的各类信息的命令。
3、jstat:查看jvm运行时的状态信息
4、jstack:查看jvm线程快照的命令
5、jinfo:查看jvm参数和动态修改部分jvm参数
常用参数:
1、-Xms:初始化堆大小
2、-Xmx:最大堆大小
3、-Xmn:新生代的内存空间大小
4、-XX:SurvivorRatio
5、-Xss:每个线程的堆栈大小
6、-XX:PermSize:设置永久代初始值
7、- XX:MaxPermSize:设置永久代最大值
目前:
fat:CMSInitiatingOccu 为70 所用收集器为CMS
堆栈信息:
4G内存:-Xms256m -Xmx2560m -Xmn960m -XX:MaxMetaspaceSize = 256m
-XX:MetaspaceSize = 256m
uat:CMSInitiatingOccu 为70 所用收集器为CMS
堆栈信息:
8G内存:-Xms5120m -Xmx5120m -Xmn1980m -XX:MaxMetaspaceSize = 512m
-XX:MetaspaceSize = 512m
pro:
MaxGCPauseMillis 为200 所用收集器为G1
16G内存:-Xms10240m -Xmx10240m -XX:MaxMetaspaceSize = 1024m
-XX:MetaspaceSize = 1024m