1.什么叫内存泄漏?

简单来说就是一个东西放在内存里的时间太长了,当你的程序都跑完了,它还存在那里。这时它是白白的占用了你的内存,累积起来占用的内存越来越多……最后就会导致JVM报错:out of memory。他占用的是我们的物理内存。

2.java内存泄漏的根本原因是?

内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。

3.java既然存在gc线程,为什么还存在内存泄漏?

这个问题,我们需要知道 GC 在什么时候回收内存对象,什么样的内存对象会被 GC 认为是“不再使用”的。

Java中对内存对象的访问,使用的是引用的方式。在 Java 代码中我们维护一个内存对象的引用变量,通过这个引用变量的值,我们可以访问到对应的内存地址中的内存对象空间。在 Java 程序中,这个引用变量本身既可以存放堆内存中,又可以放在代码栈的内存中(与基本数据类型相同)。 GC 线程会从代码栈中的引用变量开始跟踪,从而判定哪些内存是正在使用的。如果 GC 线程通过这种方式,无法跟踪到某一块堆内存,那么 GC 就认为这块内存将不再使用了(因为代码中已经无法访问这块内存了)。

通过这种有向图的内存管理方式,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。



例子1:

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

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。 在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后, 如果发生 GC , 我们创建的 Object 对象是否能够被 GC 回收呢? 答案是否定的。 因为, GC 在跟踪代码栈中的引用时, 会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空, 但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。 如果在此循环之后, Object 对象对程序已经没有任何作用, 那么我们就认为此 Java 程序发生了内存泄漏。



例子2:

如果想要看到内存溢出,可以按这样的思路去尝试一下:定义一个静态的实例变量(list或其它集合),然后在一个方法里循环往这个静态变量塞东西,直到这个实例变量撑爆你的jvm内存。很快你就能看到out of memory……

import       java.util.ArrayList;     


      import       java.util.List;     


            


      public       class       MemoryTest {     


            private       static       List list =       new       ArrayList();     


            private       static       int       count =       0      ;     


            


            public       static       void       main(String[] args)       throws       InterruptedException {     


            System.out.println(      "申请前的可用内存 = "      +getFreeMemory());     


            while      (      true      ){     


            list.add(      new       byte      [      1024      *      1024      ]);      //用实例变量申请1M内存,当方法执行完毕时,这个static的变量是不会被释放     


            count++;     


            if       (count %       100       ==       0      ) {     


            System.out.println(      "当前list.size()="      +list.size()+      ",可用内存 = "      +getFreeMemory());     


            Thread.sleep(      500      );     


            }     


            }     


            }     


            


            public       static       long       getFreeMemory() {     


            return       Runtime.getRuntime().freeMemory() / (      1024       *       1024      );     


            }     


            


      }

所以我们要慎用类变量。