1.定义
内存泄漏是指一个不再被程序使用的对象或者变量还在内存中占有存储空间。
内存溢出是指在程序执行过程中无法申请到足够的内存而导致的一种错误。
2.发生的情况
内存泄漏主要有两种情况:一是堆中申请的空间没有被释放,二是对象已经不再被使用,但还仍然在内存中保留着。
内存溢出的情况:
除了程序计数器,虚拟机内存中的其它几个运行时区域都有发生oom异常的可能
1.虚拟机栈和本地方法栈溢出
如 果线程请求的栈深度大于虚拟机所允的最大深 度,将抛出StackOverflowError 异常。
如 果 虚 拟 机 在 扩 展 栈 时 无 法 申 请 到 足 够 的 内 存 空 间 , 则 抛 出OutOfMemoryError 异常。
2.堆 溢出
一般的异常信息:java.lang.OutOfMemoryError:Java heap spaces
出现这种异常,一般手段是先通过 内存映像分析工具(如 Eclipse Memory
Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否
是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory
Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到 GC Roots 的引用链。于是就能找到泄漏对象是通过怎样的路径与 GC Roots 相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx 与-Xms)的设置是否适当。
3.方法区溢出
异常信息:java.lang.OutOfMemoryError:n PermGen space。
4.运行时常量池溢出
异常信息:java.lang.OutOfMemoryError:n PermGen space。
如 果 要 向 运 行 时 常 量 池 中 添 加 内 容 , 最 简 单 的 做 法 就 是 使 用String.intern() 这个 Native 方法。
该方法的作用是:如果池中已经包含一个等于此 String 的字符串,则返回代表池中这个字符串的 String 对象;否则,将此 String 对象包含的字符串添加到常量池中,并且返回此 String 对象的引用 。
由于 常 量 池 分 配 在 方 法 区 内 , 我 们 可 以 通 过 -XX:PermSize 和-XX:MaxPermSize 限制方法区的大小,从而间接限制其中常量池的容量。
3.发生的原因
内存泄漏:
1.静态集合类。例如hashmap和vector,如果这些容器为静态的,由于他们的生命周期和程序一致,那么容器中的对象在程序结束之前将不能被释放,从而造成内存泄漏。
长生命周期对象持有对短生命周期对象的引用。
2.各种连接。例如数据库连接,网络连接,io连接等。不使用时,没有显式的关闭,将会有大量的对象无法被回收造成内存泄漏。
3.监听器。释放对象的时候没有相应的删除监听器会造成内存泄漏。
4.变量不合理的作用域。一个变量的作用范围大于他的使用范围,很有可能造成内存泄漏。另一方面,对象没有及时的设置为null,可能会造成内存泄漏。
5.单例模式可能会造成内存泄漏。单例对象含有对其他对象的引用,单例对象以静态方式存储,在jvm整个生命周期都存在,他所引用的对象不能被释放。
内存溢出:
1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得 JVM 不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.启动参数内存值设定的过小。
4.解决方案
内存泄漏:
(重要)
1、避免在循环中创建对象。
2、尽早释放无用对象的引用。(最基本的建议)
3、 尽量少用静态变量,因为静态变量存放在永久代(方法区),永久代基本不参与垃圾回收。
4、使用字符串处理,避免使用 String, 应大量使用 StringBuffer,每一个 String对象都得独立占用内存一块区域。
内存溢出:
第一步,修改 JVM 启动参数,直接增加内存。(-Xms,-Xmx 参数一定不要忘记加。一般要将-Xms 和-Xmx 选项设置为相同,以避免在每次 GC 后调整堆的大小;建议堆的最大值设置为可用内存的最大值的 80%)。
第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
第四步,使用内存查看工具JCONSOLE查看内存使用情况。
扩展:SOF 你遇到过哪些情况 ?
基本上如果抛出 OutOfMemory 有两种原因:1.内存泄露。2.应用程序本身就是需要这么多的内存。
5.JConsole:java监视和管理控制台(查找内存泄漏)
没有内存泄漏的:内存的大小上下起伏;
有内存泄漏的:内存的大小持续的增长。