JAVA的三种溢出
一、堆内存溢出 Java heap space(老年代,java存放对象实例的地方)
发生原因:java虚拟机创建的对象太多,在垃圾回收时,分配的堆内存空间被占满,即老年代满了无法再进行回收分配。
发现问题:压测时看到抛出的java.lang.OutOfMemoryError: Java heap space。
定位问题:可在tomcat/logs 下使用命令,jmap -histo pid 查看堆内存快照信息,信息里看是否有自己公司的调用方法.
如果有,则可以确定该方法有问题,查看该方法(例如:查看该方法发现调用后没有被释放)。
解决问题:手动释放掉。
如果没找到公司内部的调用方法呢?
1.用线下工具mat分析;
2.用jmap -heap +pid 查看内存使用情况。
二、非堆内存溢出 Java lang(tomcat,持久代)
发生原因:程序中使用了大量的jar或者class,使虚拟机装载类的空间不够。
发现问题:压测时看到抛出的java.lang.OutOfMemoryError: PermGen space
定位问题:持久代空间不够。
解决问题:增大tomcat中默认的持久代空间大小,/tomcat/bin/catalina.sh 文件中增加/修改
JAVA_OPTS="-XX:PermSize=64m -XX:MaxPermSize=150m"
其中XX:PermSize是初始持久代大小,XX:MaxPermSize是最大持久代大小
三、线程栈溢出
发生原因:请求新建栈帧时,栈所剩空间小于栈帧所需空间。
例如:递归调用、大量的循环/死循环、全局变量过多、数组/List/map数据过大,都有可能造成线程栈溢出。
发现问题:1.java.lang.StackOverflowError(方法调用层次太深,内存不够新建栈帧)
2.java.lang.OutOfMemoryError:unable to create new native thread(线程太多,内存不够新建线程)
定位问题:
如果是问题1,则看是否调用了递归、使用了大量循环等;
例如:通过递归调用方法,不停的产生栈帧,一直把栈空间堆满,直到抛出异常
如果是问题2,则查看线程占用的栈空间(Xss)和线程栈的总空间。例如:
①虚拟机参数Xss调大了,每个线程占用的栈空间也就变大了,那么可以建立的线程数量必然减少;
②java总内存(Xmx)和持久代空间(-XX:MaxPermSize)过大,留给线程栈的可用空间就少,在线程栈空间不变的情况下,可创建的线程数越来越小。
解决问题:
1.优化代码;
2.调小线程占用的栈空间、增大线程栈的总空间。