JAVA OOM的概述

OOM概念

  • 全称“Out Of Memory”,通常叫做“内存溢出”,来源于java.lang.OutOfMemoryError
  • 官方说明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be madeavailable by the garbage collector.
    当JVM(java虚拟机)没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出错误(注:非exception)。
  • 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。简单来讲,内存泄露最终将导致内存溢出。

OOM分类

按错误分类
  • java.lang.OutOfMemoryError: Java heap space :java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。
  • java.lang.OutOfMemoryError: PermGen space:java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。
  • java.lang.StackOverflowError: JAVA虚拟机栈溢出,不会抛OOM error,但也是比较常见的Java内存溢出。一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。
按原因分类
  • 常发性内存溢出。发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。
  • 偶发性内存溢出。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。常发性和偶发性是相对的。对于特定的环境,偶发性的也许就变成了常发性的。所以测试环境和测试方法对检测内存泄漏至关重要。
  • 一次性内存溢出。发生内存泄漏的代码只会被执行一次,或者由于算法上的缺陷,导致总会有一块仅且一块内存发生溢出。比如,在类的构造函数中分配内存,在析构函数中却没有释放该内存,所以内存泄漏只会发生一次。
  • 隐式内存溢出。程序在运行过程中不停的分配内存,但是直到结束的时候才释放内存。严格的说这里并没有发生内存溢出,因为最终程序释放了所有申请的内存。但是对于一个服务器程序,需要运行几天,几周甚至几个月,不及时释放内存也可能导致最终耗尽系统的所有内存。所以,我们称这类内存泄漏为隐式内存泄漏。

OOM根本原因

  • 静态集合类引起内存泄漏:

HashMap、Vector等最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们一直被Vector等引用 例如:

Static Vector v = new Vector(10);
for (int i = 1; i<100; i++)
{
Object o = new Object();
v.add(o);
o = null;
}

当集合里面的对象属性被修改后,并且对象类重写了hashcode(),再调用remove()方法时不起作用。例如:

public static void main(String[] args)
{
Set set = new HashSet();
Animal a1 = new Animal(“猴子”,25);
Animal a2 = new Animal(“佩奇”,26);
Animal a3 = new Animal(“猪猪侠”,27);
set.add(a1);
set.add(a2);
set.add(a3);
System.out.println(“总共有:”+set.size()+" 个元素!"); //结果:总共有:3 个元素!
a3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变
set.remove(a3); //此时remove不掉,造成内存泄漏
set.add(a3); //重新添加,可以添加成功
System.out.println(“总共有:”+set.size()+" 个元素!"); //**结果:总共有:4 个元素!
}

  • 各种链接及数据库连接池

比如数据库连接,网络连接和io连接,除非其显式的调用了其close()方法将其连接关闭,否则是不会自动被GC 回收的。对于Resultset 和Statement 对象可以不进行显式回收,但Connection 一定要显式回收,因为Connection 在任何时候都无法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会立即为NULL。
但是如果使用连接池,情况就不一样了,除了要显式地关闭连接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另外一个也会关闭),否则就会造成大量的Statement 对象无法释放,从而引起内存泄漏。这种情况下一般都会在try里面去的连接,在finally里面释放连接。

  • 内部类和外部模块的引用

这种原理上与第一类集合是一样的,防止因为内部类和外部模块保持了引用。而且一个节点没有释放可能导致所有相关的后继类没有释放。例如A 调用了B 模块的一个方法如:

public void registerMsg(Object b);

这种调用就要非常小心了,传入了一个对象,很可能模块B就保持了对该对象的引用,这时候就需要注意模块B 是否提供相应的操作去除引用。

  • 单例模式

单例模式的错误使用是引起内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部引用,那么这个对象将不能被JVM正常回收,导致内存泄漏,考虑下面的例子:

class A{
public A(){
B.getInstance().setA(this);
}

}
//B类采用单例模式
class B{
private A a;
private static B instance=new B();
public B(){}
public static B getInstance(){
return instance;
}
public void setA(A a){
this.a=a;
}
//getter…
}

B采用singleton模式,它持有一个A对象的引用,而这个A类的对象将不能被回收。想象下如果A是个比较复杂的对象或者集合类型会发生什么情况。


作者:select you from me
来源:CSDN
原文:https://mp.csdn.net/mdeditor/94721787
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值