JVM_内存泄漏和内存溢出

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

内存泄漏

概念:

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

  • (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)堆溢出
  一般的异常信息: 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。

  • 如果要向运行时常量池中添加内容,最简单的做法就是使用 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)分散对象创建或删除的时间
  集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存,JVM 在面临这种情况时,只能进行主 GC,以回收内存或整合内存碎片从而增加主 GC 的频率。集中删除对象,道理也是一样的。 它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主 GC 的机会。

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

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

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

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

猜你喜欢

转载自blog.csdn.net/qq_17556191/article/details/94989869