java内存泄露的缘由、分析、解决

1、Java内存回收机制 
不论哪一种语言的内存分配方式,都须要返回所分配内存的真实地址,也就是返回一个指针到内存块的首地址。Java中对象是采用new或者反射的方法建立的,这些对象的建立都是在堆(Heap)中分配的,全部对象的回收都是由Java虚拟机经过垃圾回收机制完成的。GC为了可以正确释放对象,会监控每一个对象的运行情况,对他们的申请、引用、被引用、赋值等情况进行监控,Java会使用有向图的方法进行管理内存,实时监控对象是否能够达到,若是不可到达,则就将其回收,这样也能够消除引用循环的问题。在Java语言中,判断一个内存空间是否符合垃圾收集标准有两个:一个是给对象赋予了空值null,如下再没有调用过,另外一个是给对象赋予了新值,这样从新分配了内存空间。html

2、Java内存泄露引发缘由 
首先,什么是内存泄露?常常听人谈起内存泄露,但要问什么是内存泄露,没几个说得清楚。内存泄露是指无用对象(再也不使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而形成的内存空间的浪费称为内存泄露。内存泄露有时不严重且不易察觉,这样开发者就不知道存在内存泄露,但有时也会很严重,会提示你Out of memory。
那么,Java内存泄露根本缘由是什么呢?长生命周期的对象持有短生命周期对象的引用就极可能发生内存泄露,尽管短生命周期对象已经再也不须要,可是由于长生命周期对象持有它的引用而致使不能被回收,这就是java中内存泄露的发生场景。具体主要有以下几大类: 
一、静态集合类引发内存泄露: 
像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; 
}// 
在这个例子中,循环申请Object 对象,并将所申请的对象放入一个Vector 中,若是仅仅释放引用自己(o=null),那么Vector 仍然引用该对象,因此这个对象对GC 来讲是不可回收的。所以,若是对象加入到Vector 后,还必须从Vector 中删除,最简单的方法就是将Vector对象设置为null。java

二、当集合里面的对象属性被修改后,再调用remove()方法时不起做用。mysql

例: 
public static void main(String[] args) 

Set<Person> set = new HashSet<Person>(); 
Person p1 = new Person("唐僧","pwd1",25); 
Person p2 = new Person("孙悟空","pwd2",26); 
Person p3 = new Person("猪八戒","pwd3",27); 
set.add(p1); 
set.add(p2); 
set.add(p3); 
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:3 个元素! 
p3.setAge(2); //修改p3的年龄,此时p3元素对应的hashcode值发生改变 程序员

set.remove(p3); //此时remove不掉,形成内存泄漏sql

set.add(p3); //从新添加,竟然添加成功 
System.out.println("总共有:"+set.size()+" 个元素!"); //结果:总共有:4 个元素! 
for (Person person : set) 

System.out.println(person); 

}数据库

三、监听器 
在java 编程中,咱们都须要和监听器打交道,一般一个应用当中会用到不少监听器,咱们会调用一个控件的诸如addXXXListener()等方法来增长监听器,但每每在释放对象的时候却没有记住去删除这些监听器,从而增长了内存泄漏的机会。编程

四、各类链接 
好比数据库链接(dataSourse.getConnection()),网络链接(socket)和io链接,除非其显式的调用了其close()方法将其链接关闭,不然是不会自动被GC 回收的。对于Resultset 和Statement 对象能够不进行显式回收,但Connection 必定要显式回收,由于Connection 在任什么时候候都没法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会当即为NULL。可是若是使用链接池,状况就不同了,除了要显式地关闭链接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另一个也会关闭),不然就会形成大量的Statement 对象没法释放,从而引发内存泄漏。这种状况下通常都会在try里面去的链接,在finally里面释放链接。数组

五、内部类和外部模块等的引用 
内部类的引用是比较容易遗忘的一种,并且一旦没释放可能致使一系列的后继类对象没有释放。此外程序员还要当心外部模块不经意的引用,例如程序员A 负责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是个比较复杂的对象或者集合类型会发生什么状况网络

 

请参阅 http://www.cnblogs.com/jager/p/5683455.html  内存 泄露的分析和解决

 

请参阅 http://developer.51cto.com/art/201111/302465.htm

 

内存泄露避免方法:

 

Java代码 

  1. 内存泄露:  
  2.    是指在程序运行过程当中会不断的分配内存空间,那些再也不使用的内存空间应该即时回收它们,从而保证能够保证系统能够再次使用这些内存。若是存在无用的内存没有被收回来,那就是内存泄露。  
  3. 说明: 对于数组的操做,堆和栈的操做须要慎重的考虑是否存在内存泄露(出栈时没有清理最后一个元素即没有对最后一个元素置空)  
  4. 垃圾回收机制:  
  5.   a. 跟踪并监控每一个java对象,当某个对象处于不可达的状态时,就回收该对象所占用的内存。  
  6.   b. 清理内存分配、回收过程当中产生的内存碎片。  
  7.  
  8. 避免内存泄露的小技巧  
  9. a. 尽可能使用直接量,对于Byte、Short、Integer、Long、Float、Double、Bolean、Character程序不该该使用new 方式建立对象,而采用直接量建立它们。  
  10. b. 使用StringBuilder和StringBuffer进行字符串链接  
  11.    Sting和StringBuilder以及StringBuffer等均可以表明字符串,其中String字符串表明的是不可变的字符串,后二者表示可变的字符串。  
  12.    若是使用多个String对象进行字符串链接运算,在运行时可能产生大量临时字符串,这些字符串会保存在内存中从而致使程序性能降低。  
  13.   
  14. c. 尽可能少使用静态变量  
  15. 类的静态变量的生命周期和类同步的。在类不被卸载的状况下,类对应类对象会常驻内存,知道程序运行结束。  
  16. 以下代码就会存在内存泄露问题:  
  17. class  Person  
  18. {  
  19.     static Object obj = new Object();  
  20. }  
  21. d.避免在常常调用的方法、循环中建立Java对象。  
  22. e. 缓存常用的对象  
  23.    若是有些对象须要常常被用到,能够考虑把这些对象用缓存池保存起来,下次用的时候直接从池中拿。典型的就是数据链接池。  
  24.    若是系统中还有一些经常使用的基础信息,能够考虑用缓存,实现缓存的方式有以下两种:  
  25.    (1) 使用HashMap进行缓存。  
  26.    (2) 直接使用某些开源的缓存项目。  
  27.    说明:缓存设计自己就是一直以牺牲系统空间来换取运行时间的技术。     
  28. f. 考虑使用SoftReference  
  29.    当程序须要建立长度很大的数组时,能够考虑使用SoftReferene来包装数组元素,而不是直接让将数组元素来引用对象。此时SoftReference是个很好的选择:当内存足够时,它的功能等同于普通引用;当内存不足时,它会牺牲本身,释放软引用所引用的对象。  
  30. 注意:因为软引用引用对象的不肯定性(软引用所获取的对象可能为null),因此程序取出SoftReference所引用的Java以后,应该显式判断该对象是否为null; 当该对象为null时,应该重建该对象。  
相关文章
相关标签/搜索