问题描述:
如果在程序运行时,遇到内存溢出,该如何着手解决?
思路分析
内存溢出是指java程序运行时,某一块内存空间耗尽,导致OutOfMemory错误(简称OOM)。
那么根据JVM的基本结构图,我们可以看到运行时数据区主要包括方法区,堆,直接内存,还有java虚拟机栈,当对应的内存空间耗尽的时候,都会出现OOM错误。
内存溢出原因
经过分析,通常情况下,内存溢出主要有五种情况
1.堆溢出:
堆内存主要用于保存java创建的对象,当java代码中创建了大量对象,而这些对象都持有强引用,导致无法回收,当对象大小之和大于堆空间大小时,就会产生内存溢出。
2.方法区溢出:
方法区主要保存了类信息,如果java程序不停地创建新的类,就有可能导致方法区溢出。同时,方法区中还包含了运行时常量池,如果我们代码中不停地动态生成字符串,那么也有可能导致方法区溢出。
3.直接内存溢出:
java的NIO中支持直接内存的使用,也就是通过java代码,向系统申请获取一块堆外的内存空间。如果频繁地申请直接内存,就有可能触发内存溢出。(如果使用jdk64位虚拟机,一般不会出现这种问题,因为64位系统没有对程序可用最大内存做限制,而32位系统中,内存寻址空间为4G,其中用户空间2G,系统空间2G,当java进程所有内存之和大于2G时,就会出现OOM)
4.线程过多:
每一个线程的开启都会占用系统内存,因此,当线程数量过多时,也有可能导致OOM。它的情况与直接内存比较相似。
5.垃圾回收效率低:
java除了内存占用,还有内存回收,是通过GC来进行的,可以想象,如果申请内存的速度超过内存回收的速度,那么迟早都会出现OOM。
测试案例编写思路
1.暴力溢出,直接创建大量对象来耗尽分配的内存空间
//代码执行时可以添加参数-Xmx分配一个较小的堆最大空间,以便观测
public class SimpleHeapOOM {
pubic static void main(String args[]){
ArrayList<byte[]> list =new ArrayList<byte[]>();
for(int i=0;i<1024;i++){
list.add(new byte[1024*1024]);
}
}
}
2.内存泄露。具体可以参看这篇文章Java应用内存泄露排查