1、什么情况下出现堆溢出?怎么解决?
产生原因:
堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常
解决办法:
使用-XX:+HeapDumpOnOutOfMemoryError可以让java虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析,主要分析那些对象占用了内存(保存路径:-XX:HeapDumpPath=/XXX/XXX/,默认当前路径)
也可使用jmap将内存快照导出,一般检查哪些对象占用空间比较大,由此判断代码问题,没有问题的考虑调整堆参数(jmap -dump:format=b,file=XXX [pid])
2、什么情况下出现栈溢出?怎么解决?
产生原因:
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverFlowError
如果虚拟机在扩展栈时无法申请到足够的内存空间,抛出OutOfMemeoryError
栈默认深度是1M
解决办法:
StackOverFlowError一般是函数调用层级过多导致,比如死递归、死循环
OutOfMemeoryError一般是在多线程环境才会产生,一般用“减少内存的方法”,既减少最大堆和减少栈容量来换取更多的线程支持
3、什么情况下出现方法区或元数据区溢出?怎么解决?
产生原因:
jdk 1.6以前,运行时常量池还是方法区一部分,当常量池满了以后(主要是字符串变量),会抛出OOM异常
方法区和元数据区还会用于存放class的相关信息,如:类名、访问修饰符、常量池、方法、静态变量等,当工程中类比较多,而方法区或者元数据区太小,在启动的时候,也容易抛出OOM异常
解决办法:
jdk 1.7之前,通过-XX:PermSize,-XX:MaxPermSize,调整方法区的大小
jdk 1.8以后,通过-XX:MetaspaceSize,-XX:MaxMetaspaceSize,调整元数据区的大小
4、什么情况下出现本机直接内存溢出?怎么解决?
产生原因:
jdk本身很少操作直接内存,而直接内存(DirectMemory)导致溢出最大的特征是,Heap Dump文件不会看到明显异常,而程序中直接或者间接的用到了NIO
解决办法:
直接内存不受java堆大小限制,但受本机总内存的限制,可以通过MaxDirectMemorySize来设置(默认与堆内存最大值一样)