- 921. Minimum Add to Make Parentheses Valid 使括号有效的最少添加
- 915. Partition Array into Disjoint Intervals 分割数组
- 932. Beautiful Array 漂亮数组
- 940. Distinct Subsequences II 不同的子序列 II
前面两篇文章介绍了Metaspace区和Java虚拟机栈的内存溢出的分析及解决方案;一般来说,只要配置了合理的jvm参数和代码上注意一些,是不太容易发生这两块区域的内存溢出的。
真正最容易发生OOM的,是在 Java堆 中,也就是由于 平常系统中创建出来的对象实在太多了,最终导致了系统的OOM;
前面也介绍过了jvm运行时在堆中创建对象和回收的流程,在这里就只再做一个简略的对象分配回顾:
1、 系统运行时会一直不停地创建对象,这些对象会先分配到Eden区;
2、 Eden区满了之后,就会触发YGC,然后存活对象进入Survivor区;
3、 如果存活对象太多,Survivor区放不下的时候,就会通过分配担保进入到老年代中;
4、 如果每次都有对象进入到老年代,也就会很快填满老年代;在老年代满了的时候,触发FGC;
5、 如果老年代FGC之后,存活的对象还是很多,并且新生代继续往老年代进入对象;
6、 在不能放下继续进入老年代的对象的时候,导致发生堆空间的OOM;
根据上面的对象分配流程,发生堆内存溢出的原因,总结下来就是:
针对这个原因,一般来说发生堆内存溢出就主要有两种场景:
1、 系统承载高并发请求:;
因为请求量很大,就会创建很多的对象;
因为请求量很大,系统处理就会变慢,导致GC的时候很多对象都是存活的;
所以在FGC之后大量对象存活,并且继续放入,引发OOM; 2、 系统存在内存泄漏问题:;
存在内存泄漏,对于大量对象没有及时清除引用,不能被GC掉;
这些对象一直占据老年代空间,如果有新生代对象进入老年代,放不下了就会引发OOM;
这是两种主要场景,其他当然还有很多比如JVM内存分配不合理什么的,但是这些并不一定会导致堆内存溢出,可能更多的是导致频繁Full GC。
/**
*
* 堆内存溢出
*
* jvm options:
* -Xms100m -Xmx100m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+PrintGCDetails -Xloggc:log/heap-oom.log
* -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=hprof/heap-oom
*/
public class HeapOomDemo {
public static void main(String[] args) {
int count = 0;
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1 * 1024 * 1024]);
System.out.println("当前创建了 " + (++count) + "M 的对象");
}
}
}
这段代码也很简单,就是在一个while死循环里面不停地创建对象,并且使用一个list来存储对象,也就是说这些对象全部存在引用,不能被GC掉。
JVM参数为 -Xms100m -Xmx100m
,没有指定新生代大小,也就是按照默认比例分配,新生代大概30M左右,老年代大概60多M左右。
简要分析一下代码执行流程:
1、 一直创建对象放在Eden区,在放入了快要满30M的时候(假设28M),发生YGC;这28M的对象不能被回收,全部进入老年代(老年代28M);
2、 YGC之后,继续放入Eden区,再次到28M的时候,发生YGC;继续放入老年代(老年代56M);
3、 继续执行,继续放入Eden区,再次到28M的时候,想要发生YGC,但是此时老年代已经有56M了,距离老年代空间限度很小了,所以不能直接发生YGC;
4、 就需要先执行FGC清除老年代的空间,但是这56M的对象,都被引用持有,在老年代也不能被GC掉,所以老年代依然占据56M空间;
5、 此时新生代的对象还得进入老年代,但是老年代装不下了;没有办法,直接抛出OOM了;
再来看下GC日志:
从GC日志的数据呈现来看,我们上面的分析大致正确;
再来看下代码执行结果:
在创建了95M对象的时候,发生了 OutOfMemoryError: Java heap space;
发生OOM之后,往往系统会很快崩溃掉,对于所有请求都不再响应,这个时候自然要登录到服务器上检查日志文件;
在检查日志的时候,就会看到上图的 OutOfMemoryError: Java heap space
提示,自然就知道了系统发生了 堆内存溢出;
并且我们是打开了jvm的dump开关的,所以日志文件中也会显示 dump出了一份名为 java_pidxxxxx.hprof 的内存快照;
有了这个,自然是使用MAT进行分析了:
1、 打开MAT,直接查看Actions->DominatorTree:;
可以看到,这里有95个大小为1M的byte[]数组对象,他们一共占用了99%左右的内存空间; 2、 继续查看Reports->LeakSuspects:;
也能看出这里的95M对象; 3、 继续点击Seestacktrace进行查看:;
这里就看到了是 HeapOomDemo.java 类的第 21行处代码最终引发堆内存溢出的; 4、 再回到代码检查:;
list.add(new byte[1 * 1024 * 1024]);
也就是这行一直创建1M的byte[]数组对象的代码导致的…
通过MAT工具对于dump出来的内存快照进行分析,找到占用内存最多的对象,再定位到引发堆内存溢出的具体类和方法;
再结合代码进行分析,自然就能知道发生这次OOM的原因和解决办法了。
我的代码遇到了很大的困难。我正在开发一个显示歌词和和弦的应用程序。我使用两个重叠的textview分隔了和弦和歌词。 我在这个项目中遇到的问题是音高改变功能。我尽我所能向我解释得更好: 和弦总数为12
我有一个游戏并使用 Tune 作为分析库。使用最新的 Unity (5.3.4f1) 并通过 Unity 获取 apk(无 eclipse/android studio)。 我的游戏在 Play 商店
我是一名优秀的程序员,十分优秀!