Hotspot jvm的实现中,将堆内存分为了两部:新生代,老年代。在堆内存之外,还有永久代,
其中永久代实现了规范中规定的方法区。
栈溢出:出现此种情况是因为方法运行的时候,栈的深度超过了虚拟机容许的最大深度所致。
死递归:
import java.util.*;
import java.lang.*;
public class OOMTest{
public void stackOverFlowMethod(){
stackOverFlowMethod();
}
public static void main(String... args){
OOMTest oom = new OOMTest();
oom.stackOverFlowMethod();
}
}
Exception in thread "main" java.lang.StackOverflowError
at OOMTest.stackOverFlowMethod(OOMTest.java:6)
根据《java虚拟机规范》中文版:如果线程请求的栈容量超过栈允许的最大容量的话,java虚拟机将抛出一个
stackoverflow异常,如果java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,
或者在新建立现成的时候没有足够的内存去创建对应的虚拟机栈,那么java虚拟机将抛出一个outofmemory异常
堆溢出:堆溢出的时候,虚拟机将会抛出java.lang.outofmemoryError:java heap space,出现此种情况的时候
需要根据内存溢出的时候产生dump文件来具体分析。出现此种问题的时候可能是内存泄漏了,也有可能是内存溢出了。
如果内存泄漏,我们要找出泄漏的对象是怎么被gc root引用起来,然后通过引用链来具体分析原因
如果出现了内存溢出问题,这往往是程序本身需要的内存大于了我们给虚拟机配置的内存,这种情况下,我们可以采用调大
-Xmx来解决这种问题
结论:当对象大于新生代剩余内存的时候,将直接放入老年代,当老年代剩余内存还是无法放下的时候,触发垃圾回收
收集以后还是不能放下就会抛出内存溢出异常了。
持久代溢出
hotspot jvm 通过持久带实现了java虚拟机规范中的方法区,而运行时的常量池,就是保存在方法区中的,
因此持久带溢出有可能就是运行时常量池溢出,也有可能是方法区中保存的class对象没有被及时回收掉或者
class信息占用的内存超过了我们配置
List list = new ArrayList<>();
while (true) {
list.add(UUID.randomUUID().toString().intern());
System.out.println(list.get(list.size()-1));
}
字符串存储在堆中的方法区的运行时常量池 运行过程中产生了大量字符串 占用了运行时常量池的大部分空间 最终导致了方法区所在的持久代发生溢出
OutOfMemoryError:unable to create native thread
一般是下面两种情况导致的:
1.程序创建的线程数超过了操作系统的限制。对于Linux系统,我们可以通过ulimit - u 来查看此限制
2给虚拟机分配的内存过大,导致创建现成的时候的native内存太少。
总结:栈内存溢出:程序所要求的栈深度过大导致
堆内存溢出:分清内存泄漏还是内存容量不足。泄漏则看对象如何被GC Root引用。不足则设置-Xms,-Xmx参数。
持久代内存溢出:Class对象未被释放,Class对象占用信息过多,有过多的Class对象。
无法创建本地线程:总容量不变,堆内存,非堆内存设置过大,会导致能给线程的内存不足。