java内存泄露分析

来源:互联网 发布:英语口语录音软件 编辑:程序博客网 时间:2024/04/28 21:26

内存泄露

 

     所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。 java中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象编程了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:

Java代码  收藏代码
  1. public class GarbageTest {  
  2.   
  3.     public static void main(String[] args) throws IOException {  
  4.         try {  
  5.             gcTest();  
  6.         } catch (IOException e) {  
  7.             e.printStackTrace();  
  8.         }  
  9.         System.out.println("has exited gcTest!");  
  10.         System.in.read();  
  11.         System.in.read();         
  12.         System.out.println("out begin gc!");          
  13.         for(int i=0;i<100;i++)  
  14.         {  
  15.             System.gc();  
  16.             System.in.read();     
  17.             System.in.read();     
  18.         }  
  19.     }  
  20.   
  21.     private static void gcTest() throws IOException {  
  22.         System.in.read();  
  23.         System.in.read();         
  24.         Person p1 = new Person();  
  25.         System.in.read();  
  26.         System.in.read();         
  27.         Person p2 = new Person();  
  28.         p1.setMate(p2); //p1中包含p2的引用  
  29.         p2.setMate(p1); //p2中包含p1的引用  
  30.         System.out.println("before exit gctest!");  
  31.         System.in.read();  
  32.         System.in.read();         
  33.         System.gc();  
  34.         System.out.println("exit gctest!");  
  35.     }  
  36.   
  37.     private static class Person  
  38.     {  
  39.         byte[] data = new byte[20000000];  
  40.         Person mate = null;  
  41.         public void setMate(Person other)  
  42.         {  
  43.             mate = other;  
  44.         }  
  45.     }  
  46. }  

       当gcTest()方法运行完毕以后,p1和p2对象都变成了垃圾,他们都不会被根对象所找到。关于根对象请参见《JVM垃圾回收机制总结(2) :基本算法概述 》中的关于“垃圾回收从哪里开始”的讨论。

 

Java的内存泄露

 

虽然Java的垃圾回收机制较大程度的降低了内存泄露的可能性,但Java程序员仍然可能会写出发生内存泄露的代码。其原因就是一个已经不被使用的短寿命对象被一个长寿命对象(如类的静态成员对象)引用,这就使得本来要被回收的短寿命对象永远无法被回收,造成内存泄露。

 

 

内存泄露的典型情况

 

(1) 数据结构造成的短暂内存泄露问题,看下面的代码

Java代码  收藏代码
  1. public class Stack{  
  2.       private Object[] element=new Object[10];  
  3.       private int size=0;  
  4.         
  5.       public void push(Object ele){  
  6.              ensureCapacity();  
  7.              element[size++]=ele;  
  8.       }  
  9.   
  10.       public Object pop(){  
  11.              if(size==0throw new EmptyStackException();  
  12.              return element[--size]; //短暂造成内存泄露  
  13.       }  
  14.   
  15.       private void ensureCapacity(){  
  16.              if(element.length==size){  
  17.                      Object[] oldElement=element;  
  18.                      element=new Object[size*2+1];  
  19.                      System.arraycopy(oldElement,0,element,0,size);  
  20.              }  
  21.       }  
  22. }  

       上面的代码每一次pop()的时候,Stack都会弹出一个元素,在没有加入新元素之前,实际上仍然有一个引用element[x]指向了这个已经弹出的对象,因此GC是不会对其进行垃圾回收的。只有push()新元素的时候使得element[x]=newObject,才会使得以前创建的对象有可能被回收。应该把上面的pop()方法改成下面的代码就安全多了:

Java代码  收藏代码
  1. public Object pop(){  
  2.        if(element.length==size) throws EmptyStackException();  
  3.        Object o=element[--size];  
  4.        elements[size]=null;  //使得GC有机会回收这个对象  
  5.        return o;  
  6. }  

 

0 0