一:堆内存溢出
Java创建的对象一般都是分配在堆中,如果是由于过期对象没能回收(内存泄漏)或者对象过多导致放不下(内存溢出),一般报错:
Exception in thread \"main\" java.lang.OutOfMemoryError: Java heap space at java.util.Arrays.copyOf(Arrays.java:2760) at java.util.Arrays.copyOf(Arrays.java:2734) at java.util.ArrayList.ensureCapacity(ArrayList.java:167) at java.util.ArrayList.add(ArrayList.java:351) at test.java.VM.OOM.HeapOOM.main(HeapOOM.java:19)
解决这部分的异常,重点是通过内存映像分析工具分析堆的转储快照,确定异常是由于内存泄漏还是内存溢出导致的。
如果是内存泄漏导致的,则进一步查看泄漏对象到GCRoots的引用链,观察泄漏对象是通过怎样的路径与GCRoots相关联并导致垃圾回收器无法回收的;
如果是内存溢出导致的,则检测堆的大小参数(Xmx、Xms)看看能否再调大,检测是否有某些对象生命周期过长。
解决参考:https://my.oschina.net/LucasZhu/blog/1820830
二:方法区溢出(Java8之前)
方法区主要存放类的信息、静态变量、常量池等,当常量池溢出或者不停地有类动态创建并加载时,方法区也能产生OOM。
报错信息:
Exception in thread \"main\" java.lang.OutOfMemoryError: PermGen space
拓展:String.intern():如果字符串常量池已经包含一个等于此string对象的字符串,则返回该字符串;否则,将次string对象的内容加入到常量池中,并返回该对象的引用。
解决参考:
三:栈溢出(虚拟机栈、本地方法栈)
栈的异常有两种:
JVM在执行方法时就会创建方法栈,方法的递归、调用等使得其他方法不停地入栈,其他方法执行完毕就会弹出栈帧。当一个方法栈的深度大于JVM所允许的深度时就会报StackOverFlow;一般,出现StackOverFlow时就要检查代码是否有无穷递归的情况出现了。
stack length:1007Exception in thread \"main\" java.lang.StackOverflowError at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:13) at test.java.VM.OOM.JavaVMStackOF.stackLeak(JavaVMStackOF.java:14)
栈空间扩展时没有足够的内存则报OutOfMemory。
在java8中,Metaspace的出现,使我们现在不会再遇到java.lang.OutOfMemoryError: PermGen问题,但是我们要记住,这个新特性并不会使类加载导致的内存泄露就此消失。
(一)Metaspace的简单介绍
(1)内存模型:大部分类元数据都在本地内存分配,用于描述类元数据的“klasses“已经被移除。 (2) 容量:默认情况下只受本地内存限制,但是我们可以限制。1,首先不进行任何设置(需要保证堆大小),运行得到下面的截图
Metaspace进行了动态扩展,本地内存达到了300多M,加载超过5万个类后还没有出现OOM事件。接下来我们限定一下
2,虚拟机参数配置如下:-Xmx2g -Xms2g -Xmn1g -XX:+PrintGCDetails -XX:MaxMetaspaceSize=128m,运行得到下面的截图
可以看出,此时Metaspace被耗尽,将会抛出OOM事件。这里要注意MaxPermSize的区别,MaxMetaspaceSize并不会在jvm启动的时候分配一块这么大的内存出来,而MaxPermSize是会分配一块这么大的内存的。
那么问题来了,如果我们不设置MaxMetaspaceSize,那么我们能用到系统的全部内存吗??
metaspace很可能因为被无止境使用而被OS Kill,所以一般还是设置比较好。