内存溢出
1 内存溢出
1.1 堆内存溢出(outOfMemoryError:java heap space)
堆中的内存是用来生成对象实例和数组的,堆主要由新生代(eden区和两个survivor区组成)老年代、永久代。
a、由内存泄露导致,内存溢出
b、无法申请到足够的空间存放而导致的错误
1.2 方法区溢出(outOfMemoryError:PermGen space)
方法区主要存放的是类信息、常量、静态变量等,如果应用会加载很多class或使用反射、glibc(需要更多的空间保存增强的类),就很可能出现PermGen space错误。
1.3 线程栈溢出(java.lang.StackOverflowError)
线程栈时线程独有的一块内存结构,所以线程栈发生问题必定是某个线程运行时产生的错误。
一般线程栈溢出是由于死循环、递归太深或方法调用层级过多导致的,如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出上面异常。
1.4 直接内存溢出
DirectMemory可以通过-XX:MaxDirectMemorySize指定,如果不指定,默认与Java堆的最大值(-Xmx指定)一样。
NIO会使用到直接内存,你可以通过NIO来模拟,也直接使用UnSafe来分配直接内存。
2 堆内存泄露原因和排查
内存泄露指一些短生命周期的对象在完成自己职责后,本该被回收释放,却因为被一系列更长生命周期的对象引用,而不能被GC释放,我们称该对象被泄露了,随着泄露的积累,内存将被耗尽。
2.1 内存溢出的场景
1)长生命周期的对象持有短生命周期对象的引用
这是内存泄露最常见的场景,也是代码设计中经常出现的问题。
例如:在全局静态map中缓存局部变量,且没有清空操作,随着时间的推移,这个map会越来越大,造成内存泄露。
2)修改hashset中对象的参数值,且参数是计算哈希值的字段
当一个对象被存储进HashSet集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段,否则对象修改后的哈希值与最初存储进HashSet集合中时的哈希值就不同了,在这种情况下,即使在contains方法使用该对象的当前引用作为参数去HashSet集合中检索对象,也将返回找不到对象的结果,这也会导致无法从HashSet集合中删除当前对象,造成内存泄露。
3)机器的连接数和关闭时间设置
长时间开启非常耗费资源的连接,也会造成内存泄露。
2.2 解决方法
1)jmap
JDK自带的一个工具,是JVM Heap导出的必备工具。
jmap -dump:format=b,file=xxx.bin pid pid是java程序pid
此命令会将虚拟机heap镜像导成文件
2) Eclipse安装MAT插件,并打开上一步通过jmap dump出来的heap.hprof内存快照文件进行分析
然后GC roots引用链分析,查看是否存在无法自动回收的对象,如果对象都是有用的,说明没有内存泄露,检查虚拟机的堆参数(-Xmx和-Xms)
内存泄露会导致内存溢出。。。
3 申请空间不足的解决方法
3.1 java.lang.OutOfMemoryError解决方法
1) java.lang.OutOfMemoryError: PermGen space 持久代内存溢出
修改参数
JAVA_OPTS="-server -XX:PermSize=256M -XX:MaxPermSize=1024m
2) java.lang.OutOfMemoryError: Java heap space 堆内存溢出
Server端JVM建议-Xms设置与-Xmx相同,可以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn,整个堆大小=新生代大小 + 年老代大小 + 持久代大小。增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
3) java.lang.StackOverflowError:栈溢出,通常是由于递归调用造成的
-Xss128K(实际值需要经过严格的测试)。
4) java.lang.OutOfMemoryError:直接内存溢出,通常是由于分配的直接内存太大
4 避免内存泄露,需要注意
1)尽早释放无用对象的引用
好的办法是使用临时变量的时候,让引用变量在推出活动域后自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄漏。
2)程序进行字符串处理时,尽量避免使用String,而应该使用StringBuffer。
因为String类是不可变的,每一个String对象都会独立占用内存一块区域。
3)尽量少用静态变量
因为静态变量是全局的,存在方法区,GC不会回收。(用永久代实现的方法区,垃圾回收行为在这个区域是比较少出现的,垃圾回收器的主要目标是针对常量池和类型的卸载)
4)避免集中创建对象,尤其是大对象,如果可以的话尽量使用流操作
JVM会突然需要大量neicun,这时会出发GC优化系统内存环境
5)尽量运用对象池技术以提高系统性能
生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。
6)不要在经常调用的方法中创建对象,尤其忌讳在循环中创建对象
可以适当的使用hashtable,vector创建一组对象容器,然后从容器中去取这些对象,而不用每次new之后又丢弃。
7)优化配置