Java堆溢出
说明:Java堆用于存储对象实例,只要不断创建对象。且保证GC Roots到对象之间有可达的路径来避免垃圾回收机制来清除这些对象,那么在对象数量达到最大堆的容量限制之后就会产生内存溢出异常。
重点:模拟不断创建对象简单,但是需要保证创建出来的对象不被GC掉。同样的,出错时可能的状况也是如此,就是对象被创建出但是未被及时回收。-Xms堆最小值 -Xmx堆最大值。
虚拟机栈和本地方法栈溢出
抛出两种异常:
1. 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
2. 如果虚拟机在扩展栈时,无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
说明:单线程的情况下,不论是栈帧太大还是虚拟机的栈内存太小,当内存无法正常分配时,都会抛出StackOverflowError异常。
但是如果在使用多线程的情况下,会产生OutOfMemoryError异常。每个线程不断去创建线程,那么当到达上限是,就会抛出OutOfMemoryError异常。而且栈帧越大,线程上限将会越小。
原因就是:操作系统对于每个进程,分配的内存都是有限制的。虚拟机参数控制了Java堆和方法区的内存的最大值。所以剩余内存为 进程分配的最大内存(M) 减去 最大堆的内存Xmx,再减去最大方法区的MaxPermSize,
程序计数器内存很小可忽略。剩下的内存是由虚拟机栈和本地方法栈分得的。每个线程分配到的栈容量越大,那么可建立的线程数量就会很小,建立线程时就会越容易把内存耗尽。
一般情况下 栈深度可以达到1000到2000。但是如果建立了过多的线程导致了内存溢出,在不能减少线程和使用64位虚拟机的情况下,只能通过减少最大堆和减少栈容量来换取更多线程。
方法区和运行时常量区溢出
运行时常量池是方法区的一部分,在JDK1.6及以前的版本中,因为常量池分配在永久代中,我们可以通过-XX:PermSize和-XX:MaxPermSize来限制方法区大小,从而间接限制其中常量池的容量。
代码如下:
1 public static void main(String[] args) {2 Listlist = new ArrayList ();3 int i = 1;4 while (true) {5 ist.add(String.valueOf(i++).intern());6 }7 }
会抛出OutOfMemoryError异常。
但是在JDK1.7以后,while循环会一直进行下去。
方法区的用于存储Class相关的信息,比如类名、访问修饰符、常量池、字段描述、方法描述等。对于此区域的测试,可以产生大量的类去填满方法区,直到溢出。