内存溢出

瘦欲@ 提交于 2019-12-21 00:41:48

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)优化配置

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!