内存溢出解析

一、内存溢出的原理

 

在C/C++语言中,需要开发者自己管理内存,通过malloc/new分配内存空间,并且需要写配对的delete/free代码。

在Java语言中,由Java虚拟机管理内存。

 

 

 

冯诺伊曼结构

 

 

Java虚拟机运行时数据区

 

 

Java线程共享空间

 

二、哪些区域会内存溢出

1.Java堆

内存溢出例子

 public class OutOfMemoryTest {

    public static void main(String[] args) {

        List<byte[]> list = new ArrayList<byte[]>();

        for (int i = 0; i < 10240; i++) {

            list.add(new byte[1024 * 1024]);

        }

    }

}

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

 

Minor GC 会清理年轻代的内存

Major GC 是清理老年代

Full GC 是清理整个堆空间—包括年轻代和老年代

在新生代中,每次垃圾收集时都会发现大批对象死去,选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代因为对象存活率高,没有额外空间对它进行分配担保,就需要使用“标记-清理”或者“标记-整理”算法来进行回收。CMS是基于“标记-清除”算法实现的收集器。

2.永久代

用语存储类信息、常量、静态变量、即使编译器编译以后的代码

永久代的垃圾收集主要回收两部分内容:废弃的常量和无用的类。
无用的类:类需要同时满足下面3个条件才能算是“无用的类”:

1.该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。

2.加载该类的ClassLoader已经被回收

3.该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。

虚拟机可以对满足上述3个条件的无用类进行回收,是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制。

在大量使用反射、动态代理、CGlib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载的功能,以保证永久代不会溢出

3.Direct Memory

NIO引入基于通道和缓冲区的I/O方式,使用native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作

4.线程堆栈

每个方法在执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

5.Socket缓冲区

每个Socket连接都有Receive和Send两个缓冲区

6.JNI代码

7.虚拟机和GC

三、哪些情况下会内存溢出

1.内存中加载的数据量过于庞大

  一次从数据库取出过多数据

 使用BlockingQueue无界队列,消费者速度小于生产者速度

2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收

3.代码中存在死循环或循环产生过多重复的对象实体

4.使用的第三方软件中的BUG

5.启动参数内存值设定的过小

-Xms :初始堆大小
-Xmx :最大堆大小
-Xmn:新生代大小。通常为 Xmx 的 1/3 或 1/4。新生代 = Eden + 2 个 Survivor 空间。实际可用空间为 = Eden + 1 个 Survivor。
-XX:NewSize=n :设置年轻代
-XX:NewRatio=n: 设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3
-XX:SurvivorRatio=n :年轻代中Eden区与两个Survivor区的比值。Survivor区有两个。如果为3,表示Eden:Survivor=3:2
-XX:PermSize=n 永久代(方法区)的初始大小
-XX:MaxPermSize=n :设置永久代大小
-Xss 设定栈容量;
-XX:PretenureSizeThreshold (该设置只对Serial和ParNew收集器生效) 可以设置进入老生代的大小限制
-XX:MaxTenuringThreshold=1(默认15)垃圾最大年龄 如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率
该参数只有在串行GC时才有效.

-XX:MaxDirectMemorySize  设置本机直接内存大小

6.StackOverflow

关于虚拟机栈和本地方法栈,Java虚拟机规范中描述了两种异常:

如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError

如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError

7.递归次数过多

8.内存泄漏

1.ThreadLocalMap的生命周期跟Thread一样长,如果没有手动删除对应key就会导致内存泄漏

2.

LinkedBlockingQueue

private E dequeue() {

    Node<E> h = head;

    Node<E> first = h.next;

    h.next = h; // help GC

    head = first;

    E x = first.item;

    first.item = null;

    return x;

}

如果没有first.item = null;那么对象就不会被回收,造成内存泄漏

3.数据库连接,网络连接和io连接完成以后没有调用close()方法。

9.线程过多。

四、哪些习惯可以尽量避免内存溢出

1、尽早释放无用对象的引用。好的办法是使用临时变量的时候,让引用变量在退出活动域后,自动设置为null,暗示垃圾收集器来收集该对象,防止发生内存泄露。

对于仍然有指针指向的实例,jvm就不会回收该资源,因为垃圾回收会将值为null的对象作为垃圾,提高GC回收机制效率;

2、我们的程序里不可避免大量使用字符串处理,避免使用String,应大量使用StringBuffer,每一个String对象都得独立占用内存一块区域;

String str = "aaa";

String str2 = "bbb";

String str3 = str + str2;

假如执行此次之后str ,str2以后再不被调用,那它就会被放在内存中等待Java的gc去回收,建议在使用字符串时能使用StringBuffer就不要用String,可以省不少开销。

3、尽量少用静态变量,因为静态变量是全局的,GC不会回收的。

4、避免集中创建大对象,JVM会突然需要大量内存,这时必然会触发GC优化系统内存环境

使用jspsmartUpload作文件上传,运行过程中经常出现java.outofMemoryError的错误

检查之后发现问题:组件里的代码

m_totalBytes = m_request.getContentLength();

m_binArray = new byte[m_totalBytes];

问题原因是totalBytes这个变量得到的数极大,导致该数组分配了很多内存空间,而且该数组不能及时释放。

5、尽量运用对象池技术以提高系统性能;生命周期长的对象拥有生命周期短的对象时容易引发内存泄漏,例如大集合对象拥有大数据量的业务对象的时候,可以考虑分块进行处理,然后解决一块释放一块的策略。

6、尽量不在经常调用的方法中创建对象,尤其是在循环中创建对象。可以适当的使用Hashtable创建一组对象容器,然后从容器中去取那些对象,而不用每次new之后又丢弃。

7、开启大型文件或跟数据库一次拿了太多的数据,造成 Out Of Memory Error 的状况,这时就大概要计算一下数据量的最大值是多少,并且设定所需最小及最大的内存空间值。

select * from information_schema.TABLES

where information_schema.TABLES.TABLE_SCHEMA='databasename'

and information_schema.TABLES.TABLE_NAME='tablename'

 

五、如何检测内存溢出

1.监测内存使用率,当内存使用率达到阈值时报警,人工介入。

2.jmap命令用于生成堆转储快照,jstack命令用于生成虚拟机当前时刻的线程快照。

3.可视化工具JConsole和VisualVM、Mission Control、MAT

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值