java内存回收问题

作者: littlersmall | 来源:发表于2016-01-19 12:59 被阅读219次

首先看一下下面两段代码有什么区别:

{
    new A().test();
} 

{
    A a = new A();
    a.test();
}

当时去问我们项目经理,他坚持认为这两种方式是一样的,个人习惯不同造成的不同写法而已。就功能上来说,都调用了test()函数,确实没什么区别,但是,如果考虑了内存回收,这两种写法就有很大的不同。
我们可以把这个例子更具体一点,如下:

{
    //mark 1
    new A().test();
    //mark 2
    new A().test();
    //mark 3
    .....
}

第一种写法,在mark2处,A的内存已经可以被交给垃圾回收器回收了,也就是说在mark2处,可用内存和mark1处的可用内存完全相同。

{
    //mark 1
    A a = new A();
    //mark 2
    A b = new A();

    a.test();
    b.test();
}

第二种写法在mark 2处的可用内存和mark1处的可用内存是不同的,如果A类使用很大的空间,那么在mark2这里会抛出内存溢出异常,相反,第一种写法却没有这种问题。
下面的测试代码证明了两种写法的区别

class MemoryTest
{
 int a[] = new int[10 * 1024 * 1024 * 10];
 static int b = 0;
 
 MemoryTest()
 {
  b++;
  a[0] = a[1] = 2;
 }
 
 void Test()
 {
  System.out.println("12345 + " + b); 
 }
}

public class TestJava
{
 public static void main(String[] args)
 throws Exception
 {
  //works well
  new MemoryTest().Test();
  
  //the gc collected the memory so it can be reuse
  new MemoryTest().Test();
  
  MemoryTest c = new MemoryTest();
  
  //if cancel this comment, there will be a memory exception
  //that means there's not enough memory for d
  /*MemoryTest d = new MemoryTest();*/
  
  System.out.println("end test");
 }
}

造成这种问题,主要还是java的内存回收机制,当java发现可用内存不足时,会调用内存回收器,内存回收器会去遍历当前线程栈,然后根据栈中的引用确定当前被使用的内存,将没有被遍历到的内存释放,在上面的例子中,b处于栈上,无法被回收,因此在c申请新内存是异常。b和c指向的内存要等到出了作用域(最近的大括号)才可以被回收。
这个问题解决后,马上又有一个新的问题,第一种写法中我们调用 new A().test(); 如果这个函数执行时间非常长,如何保证在执行过程中A的内存不会被回收(没有显式处于栈上的引用指向)。
考虑到c++的临时变量,所以猜想java的编译器会将new A().test();这段代码做如下处理:

{
   {
        //mark 1
        A temp = new A();
        temp.test();
   }
   //mark 2
}

在mark1处,从栈上分配temp引用指向堆中的A,之后,在mark2处,由于temp离开他自己的作用域,则栈上内存释放,也就是说栈上不再具有指向A的引用,使得A内存可被回收。

结论
推荐使用 new A().test();这样的写法,在一定程度上可以节省当前内存。
(原文时间2013-1-30)

相关文章

网友评论

本文标题:java内存回收问题

本文链接:https://www.haomeiwen.com/subject/yigkkttx.html