Java内存泄漏
1、什么是内存泄漏
当某些对象不再被应用程序所使用,但是由于仍然被引用而导致垃圾收集器不能释放(Remove,移除)
2、引起内存泄漏的原因
长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露(老年代引用新生代)
2.1 当集合里面的对象属性被修改后,由于hashcode改变当再调用remove()方法时不起作用
1 import java.util.HashSet; 2 import java.util.Set; 3 4 /** 5 * Created by xiaoxiao7 on 2016/11/9. 6 */ 7 public class Test01 { 8 9 public static void main(String[] args) { 10 Set<String> set = new HashSet<String>(); 11 String str1 = new String("aaa"); 12 String str2 = new String("bbb"); 13 String str3 = new String("ccc"); 14 set.add(str1); 15 set.add(str2); 16 set.add(str3); 17 System.out.println("Before: " + set.size()); 18 str3 = "ddd"; 19 set.remove(str3); 20 set.add(str3); 21 System.out.println("After: " + set.size()); 22 for (String str : set) { 23 System.out.println(str); 24 } 25 } 26 }
2.2 静态集合类引起内存泄漏
1 import java.util.HashSet; 2 import java.util.Set; 3 import java.util.Vector; 4 5 /** 6 * Created by xiaoxiao7 on 2016/11/9. 7 */ 8 public class Test01 { 9 10 static Vector v = new Vector(10); 11 12 public static void main(String[] args) { 13 for (int i = 1; i<100; i++) 14 { 15 Object o = new Object(); 16 v.add(o); 17 o = null; 18 } 19 } 20 }
2.3 监听器
在开发过程中通常会调用一个控件的诸如addXXXListener()等方法来增加监听器,但往往在释放对象时没有删除监听器,从而增加了内存泄漏的机会
2.4 各种连接
如数据库连接(dataSource.getConnection())、网络连接(socket)和IO连接,除非显示的调用了close()方法将其连接关闭,否则是不会自动被GC回收的
2.5 单例模式
不正确的使用单例模式是引起内存泄漏的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被JVM正常回收,导致内存泄漏
1 public class A { 2 public A() { 3 B.getInstance().setA(this); 4 } 5 } 6 7 8 9 public class B { 10 private A a; 11 private static final B instance = new B(); 12 private B() {} 13 14 public static B getInstance() { 15 return instance; 16 } 17 18 public A getA() { 19 return a; 20 } 21 22 public void setA(A a) { 23 this.a = a; 24 } 25 }
其中,B采用单例模式,它持有一个A对象的引用,而这个A类对象将不能被GC回收
3、如何预防内存泄漏
- 当心集合类,比如HashMap、ArrayList等,因为容器是最容易发生内存泄漏的地方。当集合对象被声明为static时,他们的生命周期一般和整个应用程序一样长
- 注意事件监听和回调。当注册的监听器不再使用以后,如果没有被注销,那么很可能会发生内存泄漏