JVM_内存泄漏和内存溢出

内存泄漏

概念:

一个不再被程序使用的对象或者变量还在内存中占有存储空间。

(1)堆中申请的空间没有被释放

(2)对象不在使用但还在内存中保留

内存泄漏的原因:

(1)静态集合类,如hashmap和vector,如果容器为静态,她们的生命周期与程序一致。

(2)各种连接,如数据库连接,IO连接

(3)监听器:通常一个应用中会用到多个监听器,但是在释放对象的同时往往没有相应的删除监听器

(4)变量不合理的作用域。一方面一个变量的定义作用范围大于其使用范围,很可能造成内存泄漏。另一方面如果没有及时把对象设置为null,很可能导致内存泄漏。

(5)单例模式:一直存在着一个对对象的引用,并且以一个静态变量的方式存储,因此它在JVM整个生命周期都存在。

内存泄漏解决方案:

(1)避免在循环中创建对象

(2) 尽早释放无用的对象引用

(3)尽量少用静态变量

(4)使用字符串处理,避免使用String,应大量使用StringBuffer,因为每个String对象都得独立占用内存一块区域

内存溢出OOM

概念:

程序运行过程中无法申请到足够的内存而导致的一种错误,除了程序计数器外,其他几个运行区都有OOM的可能。

内存溢出情况:

(1)虚拟机栈和本地方法栈溢出

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

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

(2)堆溢出

1
2
3
4
5
6
> 一般的异常信息: java.lang.OutOfMemoryError:Java heap spaces
>  出现这种异常,一般手段是先通过内存映像分析工具(如 Eclipse Memory Analyzer)对 dump 出来的堆转存快照进行分析,
>  重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏还是内存溢出。
>     1.如果是内存泄漏, 可进一步通过工具查看泄漏对象到 GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与 GC Roots 相关联并导致垃圾收集器无法自动回收。
>     2.如果不存在泄漏, 那就应该检查虚拟机的参数(-Xmx 与-Xms)的设置是否适当。
>

(3)方法区溢出

异常信息: java.lang.OutOfMemoryError:PermGen space。

(4)运行时常量池溢出

异常信息: java.lang.OutOfMemoryError:PermGen space。  
1
2
3
4
5
> 如果要向运行时常量池中添加内容,最简单的做法就是使用 String.intern()这个Native 方法。
>     该方法的作用是:如果池中已经包含一个等于此 String 的字符串, 则返回代表池中这个字符串的 String 对象;
>     否则,将此 String 对象包含的字符串添加到常量池中, 并且返回此 String 对象的引用 。
> 由于常量池分配在方法区内,我们可以通过-XX:PermSize 和 -XX:MaxPermSize 限制方法区的大小, 从而间接限制其中常量池的容量。
>
内存溢出原因:

1.内存中加载的数据量过于庞大, 如一次从数据库取出过多数据;

2.集合类中有对对象的引用, 使用完后未清空, 使得 JVM 不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.启动参数内存值设定的过小。

内存溢出解决方法:

(1) 修改 JVM 启动参数, 直接增加内存。 (-Xms, -Xmx 参数一定不要忘记加。一般要将-Xms 和-Xmx 选项设置为相同, 以避免在每次 GC 后调整堆的大小; 建 议堆的最大值设置为可用内存的最大值的 80%)。

(2) 检查错误日志, 查看“OutOfMemory” 错误前是否有其它异常或错误。

(3)对代码进行走查和分析, 找出可能发生内存溢出的位置

(4) 使用内存查看工具动态查看内存使用情况(Jconsole)。

减少gc次数的方法:

(1)对象不用时最好显式置为 Null

一般而言,为 Null 的对象都会被作为垃圾处理,所以将不用的对象显式地设  为 Null,有利于 GC 收集器判定垃圾,从而提高了 GC 的效率。  

(2)尽量少用 System.gc()

此函数建议 JVM进行主 GC,虽然只是建议而非一定,但很多情况下它会触发  主 GC,从而增加主 GC 的频率,也即增加了间歇性停顿的次数。  

(3)尽量少用静态变量

静态变量属于全局变量,不会被 GC 回收,它们会一直占用内存。  

(4) 尽量使用 StringBuffer,而不用 String 来累加字符串。
由于 String 是固定长的字符串对象,累加 String 对象时,并非在一个 String 对象中扩增,而是重新创建新的 String 对象,如 Str5=Str1+Str2+Str3+Str4,这条 语句执行过程中会产生多个垃圾对象,因为对次作“+”操作时都必须创建新 的 String 对象,但这些过渡对象对系统来说是没有实际意义的,只会增加更多 的垃圾。 避免这种情况可以改用 StringBuffer 来累加字符串,因 StringBuffer 是可变长的,它在原有基础上进行扩增,不会产生中间对象。
(5)分散对象创建或删除的时间

1
2
3
4
> 集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM 在面临这种情况时,只能进行主 GC,以回收内存或整合
> 内存碎片从而增加主 GC 的频率。集中删除对象,道理也是一样的。 它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大
> 增加了下一次创建新对象时强制主 GC 的机会。
>

(6) 尽量少用 finalize 函数。 因为它会加大 GC 的工作量, 因此尽量少用finalize 方式回收资源。

(7) 如果需要使用经常用到的图片, 可以使用软引用类型, 它可以尽可能

(8)能用基本类型如 int,long,就不用 Integer,Long 对象

基本类型变量占用的内存资源比相应包装类对象占用的少得多,如果没有必要,最好使用基本变量。  

(9) 增大-Xmx 的值。

文章目录
  1. 1. 内存泄漏
    1. 1.0.0.0.1. 概念:
    2. 1.0.0.0.2. 内存泄漏的原因:
    3. 1.0.0.0.3. 内存泄漏解决方案:
  • 2. 内存溢出OOM
    1. 2.0.0.0.1. 概念:
    2. 2.0.0.0.2. 内存溢出情况:
    3. 2.0.0.0.3. 内存溢出原因:
    4. 2.0.0.0.4. 内存溢出解决方法:
  • 3. 减少gc次数的方法:
  • | 139.6k