Java内存溢出、泄露问题追踪

课题背景:

      内存溢出和泄露在日常项目的开发过程中,出问题导致系统奔溃并不算常见,但是一旦出现,总避免不了一阵头疼,此类问题确实难以发现并解决。笔者参考了几本资料做了以下的总结。

1. 首先先简单谈谈什么是内存泄露和内存溢出(这块纯粹是为了加深笔者的记忆,可以忽略): 

     内存泄露(Memory Leak):简单的说,就是存在无法释放但是无用的内存空间。通常在java中,此类空间会被GC自动回收,但是在GC回收的前提是,此空间不再存在引用。滥用static和singleton对象,就可能导致无用对象空间长期占用。
     内存溢出(Memory Overflow):简单的说,就是内存空间耗尽,无法在申请到更多的空间。

2. 堆空间:

     常用的堆空间配置(这些配置大家应该都熟悉,增加此部分也完全是为了加深笔者的记忆,可以忽略):
           -Xms20m:堆的初始空间大小。
           -Xmx20m:堆的可夸张空间容量大小。
     另外再唠叨两个虚拟机配置:
            -XX:+HeapDumpOnOutOfMemoryError 堆溢出时,生成dump分析文件。
            -XX:+UseGCOverheadLimit  作为1.6以后的GC安全策略,默认被开启,用于禁止GC过程无限制的执行,如果过于频繁,就直接抛出OOM。
     堆空间常见的几种异常:
            java.lang.OutOfMemoryError: Java heap space,无法申请到更多的内存空间。
            java.lang.OutOfMemoryError: GC overhead limit exceeded,1.6以后,开启UserGCOverheadLimit之后,当GC的频繁的发生会引发此类问题,一般的原因是GC频繁回收的都是小对象,而程序又在持续的申请堆空间。
      分析过程(前提当然是准备好了dump文件):
            ① 通过对上面的堆异常的描述,能对问题做个分类,是因为内存空间被耗尽,还是频繁的发生GC回收。
            ② 识别发生了内存泄露还是内存溢出引发的系统奔溃(通常情况下,这是不够的,因为最终引发奔溃的未必是奔溃的根本原因),借助VisualVM和Eclipse Memory Analyzer(后面简称EMA),分辨出"哪些对象是必须存在的?"和"哪些对象无需存在的?"。发生内存泄露,那么就需要修改原有的设计,减少对象的生命周期。发生内存溢出,通常情况下堆堆空间肯定是最容易解决的方案,但是有些时候,堆了空间仍然会导致此类问题,也有可能引发其他的问题,毕竟内存容量毕竟是有限的。说起来简单,这个步骤估计是要不少的时间去分析。

3. 栈空间:

     这部分空间又分为虚拟机栈和本地方法栈。
     常用的虚拟机配置:
           -Xss128k: 设置栈容量,此处的栈容量并不是总的栈空间大小,而是每个线程具备的容量大小。事实上JVM堆栈的容量设了下限,记不清了可能是104K,这点可以让读者自己去查下。笔者平时没有尝试去挑战JVM的习惯。
     常见的异常:
           java.lang.StackOverflowError: 分配好的栈容量被消耗殆尽,该线程无法获取到更多的栈空间的时候。
           java.lang.OutOfMemoryError: Unale to create new native thread。
     分析:
          先分析栈的空间定义(32位window):
           2G(32位window设置的上限) = Xmx + MaxPermSize(永久区容量,后面会介绍到) + 虚拟机进程本身消耗 + 程序计数器 + 线程数 * Xss + 本地直接内存。
           如果没有考虑到总内存的限制,胡乱的设置空间,意外的内存溢出就可能发生。Xss设置的栈容量消耗完的时候,就会出现StackOverflowError,而一旦线程数上来了,总的栈容量超过了上限(线程数 * Xss),那么就会出现OMM,所以,出现StackOverflowError常见于递归,而OOM则是线程过多。一般来说出现StackOverflowError快速的解决办法是加大Xss,如果硬件允许的情况下可以迁移到64位的JVM,当然改建代码自然是最好的办法。出现OOM,则可以调整其他内存容量,使栈总容量上去,同样,允许情况下,降低线程数也是个不错的选择。

4. 直接内存(Direct Memory):

      这部分空间也是经常被忽略的一层,在NIO有可能直接的操作这部分空间。
      常用的配置:
             -XX:HeapDumpOnOfMemoryError: 用于设置最大的直接内存空间,如果不指定,则默认与java最大堆容量大小一样(Xss)
      常见的异常:
            java.lang.OutOfMemoryError:    很多时候不会有提示的信息,当然不是说其他的空间都会提示,只是常不常见的问题。
            java.lang.OutOfMemoryError: Direct buffer memory.   比较容易的看出是直接内存的空间不足。
      分析:
           ① 出现此类问题的时候,堆转储文件是不会生成的(就是前面说的dump文件)。
           ② 此类问题一般出现在大量使用NIO的时候。
            通常次累问题不容解决,除了修改直接内存的空间以外,减小其他的内存容量也是必要的。

4. 方法区内存:

      相关配置:
           -XX:PermSize=10m:永久区初始空间
           -XX:MaxPermSize=10m:永久区最大可扩展空间
      常见的异常:
          java.lang.OutOfMemoryError:PermGen space
      分析:
          方法区用于存放Class的相关信息,常量,静态变量,即使编译后的代码,1.6及1.6以前jdk的字符串常量池。 所以程序中大量使用动态生成class对象(动态代理,cglib,反射,JSP,OSGI)的场景下,比较常见。当然1.6的jdk,大量使用字符串常量也有可能出现。出现这种问题,自然也是适当调整之前的公式还有改进代码实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值