在Java虚拟机规范的描述中,除了程序计算器外,虚拟机内存的其他区域都会发生OutOfMemoryError异常。
Java堆溢出
实现:不断创建对象,保证对象路径可达避免垃圾回收,达到最大堆的容量限制
通过虚拟机启动参数 -Xms和-Xmx设置堆的大小。
报错信息里面包含“Java heap space”
分析
设置-XX:+HeapDumpOnOutOfMemoryError参数可以让虚拟机出现内存溢出时生成Dump文件。(通过Eclipse Memory Analyzer分析)
1、内存泄漏
通过分析工具查看泄漏对象的GC Root的应用链(导致无法垃圾回收)
2、内存不泄漏
a、调整虚拟机的堆参数 b、减少程序内存消耗
虚拟机栈和本地方法栈溢出
存在以下两种异常
1、如果线程请求的栈深度大于虚拟机所允许的最大深度,抛出StackOverflowError异常
2、如果虚拟机的扩展栈时无法申请到足够的内存空间,抛出OutOfMemoryError异常
在《深入理解Java虚拟机》书籍中,作者使用两种方法都无法抛出OutOfMemoryError异常
1、通过-Xss参数减少栈内存容量
2、定义大量的本地变量,增大此方法帧中本地变量表的长度。
分析
系统给进程内存是有限制,例如32位系统为2GB。
这部分空间减去 Java堆和方法区,然后再减去Xmx(最大堆容量),再减去MaxPermSize(最大方法区容量)(忽略程序计数器),剩下由虚拟机栈和本地方法栈瓜分。
这样如果每个线程的栈分配内容越大,反而容易出现溢出
(栈深度在大多数情况之下都足够哟过,可大1000~2000)
多线程导致内存溢出,在不减少线程数情况之下。可以减少最大堆和减少栈容量来获取更多的线程。
方法区和运行时常量池溢出
常量池
注意String.intern()在各版本的差异
是一个Native方法,如果字符串常量池中已经包含一个等于String对象的字符串,则返回代表常量池中合格字符串的String对象。
1、JDK1.6:会把首次晕倒的字符串实例复制到永生代(后续JDK版本逐步去除)中,返回的也是永生代中这个字符串实例的引用
2、JDK1.7:不会在复制实例,只是在常量池中记录首次出现的实例引用。
该部分详情可以查看我另外一篇文章:
String.intern内部实现
所以:JDK1.6可以通过intern实现常量池溢出,JDK1.7则无法实现。
方法区
经常动态生成大量Class的应用(类回收条件比价苛刻)
1、CGLib字节码技术和动态语言(Groovy)
2、大量Jsp文件或动态生成Jsp(Jsp第一次运行需要编译成Java类)
3、基于OSGi应用(不同加载器加载同个类)