关于JAVA中的Garbage Collection和finalize()

关于JAVA中的Garbage Collection和finalize()


 Java的内存释放涉及到Garbage Collection的概念,那么到底什么是Garbage Collection呢?中文的翻译是“垃圾收集”。再看到一些java书籍时,对Garbage Collection讲解的并不是非常清楚,有些书干脆就只字不提。我在学习java的过程中,这个问题困扰了我相当长的一段时间,为此我总结了一下关于java中内存释放的一些概念和用法。
在java中,关于释放内存的方式与C++有很大区别,对于一个熟悉C++的程序员来说,在写java语句时,要记住以下两点:
(1)、Garbage Collection不是析构函数。
(2)、你的对象不一定会被释放内存。
首先谈一下Garbage Collection的用处。顾名思义,Garbage Collection就是回收不再为程序使用的内存。那么,它在什么时候被调用呢?第一、当程序占用的内存过多时;第二、人为的调用System.gc()方法时。先举一个简单的例子:

class Chair{
 static boolean flag=true;
 static int finalizeNum=0;
 static int constructNum=0;
 Chair()
 {
  constructNum++;
 }
 public void finalize(){
  flag=false;
  finalizeNum++;
 }
}
public class Garbage{

 public static void main(String []args){
  while(Chair.flag){
   new Chair();
  }
  System.out.println("You have created "+
    Chair.constructNum+" objects of Chair! And have finalized "+
    Chair.finalizeNum+" objects ");
 }
}
输出结果:
You have created 8557 objects of Chair! And have finalized 5 objects
大家可以看到,我在main函数中写了一个循环,在这个循环中new一个Chair的对象,这个循环的判断条件是Chair.flag,它是一个boolean型的变量,初试值为true,你可能会问:循环什么时候结束?在类Chair中有一个函数名为finalize()(我在后面会对这个函数予以详细的解释),在这个函数中有一条语句flag=false;。对就是这条语句使得主函数退出while循环。那么,什么时候调用finalize()函数呢?这就是Garbage Collection的问题之所在。我们刚才提到过,java的垃圾收集会在程序占用内存非常多的时候运行,当垃圾收集运行的时候,他首先会调用finalize()方法,然后在释放程序中不用的内存。我们在每次循环时,都new一个对象,目的就是让他占用一定的内存,当循环到一定的次数时,系统内存必然不够用,这样Garbage Collection就会运行,然后调用finalize()。
再来看一下程序,类Chair内部有三个静态变量,flag已经说过,constructNum在构造函数中加一,finalizeNum在finalize()方法中加一,也就是说,constructNum记录的是构造的Chair对象的数目,finalizeNum记录的是被释放的对象的个数。那么看一下运行结果,构造了8557(当然,这个数字随着环境的不同而不同)个对象,释放了5个对象,这是为什么?为什么不是每个对象都释放呢?这是因为,在此例中Garbage Collection只释放超出内存容量的部分,当内存无法容纳这么多的对象的时候,垃圾收集会释放一个一个的释放,直到内存允许之后。所以,它并不是释放所有的对象。
你们是否还记得本章开头曾提到过Garbage Collection运行的两种情况,上面这个例子显示的是第一种:当程序占用的内存过多时。那么第二种是什么意思呢?我们再来看一个例子:
class Chair{
 static boolean flag=true;
 static int finalizeNum=0;
 static int constructNum=0;
 Chair()
 {
  constructNum++;
 }
 public void finalize(){
  flag=false;
  finalizeNum++;
 }
}
public class Garbage{
 public static void main(String []args){
  while(Chair.flag){
   new Chair();
  }
 System.gc();
 System.out.println("You have created "+
Chair.constructNum+" objects of Chair! And have finalized "+
  Chair.finalizeNum+" objects ");
 }
}
这个例子与上面第一个例子只有一行的区别,就是在main函数中while循环结束后多了一条System.gc()语句,它的作用是强制程序释放所有不用的内存,所以这个程序的运行结果是:
You have created 8557 objects of Chair! And have finalized 8557 objects,再次强调,你的运行结果不一定非得是8557这个数字,它根据当前的环境而变化。
好了,我们已经看到了Garbage Collection是如何运行的,如果你有一定的研究精神的话,你一定会问:什么样的对象才会被“垃圾收集”呢?是不是每定义一个对象,都要被“收集”呢?好,如果你对此有兴趣,请往下看(如果你只是想知道Garbage Collection如何运行,不想了解更多的东西,下面的内容并不适合你)。
我喜欢以例子的方式讲解,首先请看一个简单的例子:

class MyClass{
 private String name;
 public MyClass(String name){
  this.name=name;
  System.out.println(name+" is created.");
 }
 protected void finalize() throws Throwable {
  // TODO Auto-generated method stub
  System.out.println("finalize "+name);
 }
}
public class Garbage{
 public static void main(String []args){
  MyClass myClass1=new MyClass("myClass1");
  new MyClass("myClass2");
  System.gc();
 }
}
输出结果是:
myClass1 is created.
myClass2 is created.
finalize myClass2
可以看到,对象myClass1和myClass2都成功的创建,但是当调用System.gc()时,只是回收了对象myClass2,而myClass1却没有得到回收,为什么呢?回忆本章开始讲到的Garbage Collectino运行的两个条件,第一、当程序占用的内存过多时;第二、人为的调用System.gc()方法时。这个例子中,显然没有涉及到第一个条件(除非你的内存非常小,连两个对象都装不下,这怎么可能?),那么第二个条件是怎么工作的呢?在java帮助文档中对System.gc()进行了说明,System.gc()用来回收没有用的(也就是死掉的)对象所占用的内存。那么,什么是没有用的对象呢?我们知道,每个对象都有一个指向它的引用(在C++中习惯说指针,但java里没有指针),通过引用可以找到对应的对象从而提取相应的内存中的内容。在本例中,由于myClass2没有任何引用指向它(实际上,myClass2称为匿名对象),所以它是一个没有用的对象(不会被用到),所以当调用System.gc()来回收“垃圾”时,JVM(java虚拟机)就把它视为一快“垃圾”,从而释放调它,在垃圾回收时,会先调用finalize()函数,所以有了上面的一行输出(finalize myClass2)。
那么,当程序结束时,应该释放所有对象占用的内存,那么此时为什么没有调用myClass1的finalize()呢?其实,当程序结束时,JVM不会回收那些变量(如myClass1),而是把它交给操作系统,由操作系统来处理。因为Garbage Collection毕竟会消耗一定的内存,如果所有的对象都有Garbage Collection来回收,会很浪费资源,这也是Java独到之处。
好了,关于Garbage Collection的话题已经说的差不多了,为了加深理解,再来看一个小例子:
//Garbage.java
class MyClass{
 private String name;
 public MyClass(String name){
  this.name=name;
  System.out.println(name+" is created.");
 }
 protected void finalize() throws Throwable {
  // TODO Auto-generated method stub
  System.out.println("finalize "+name);
 }
}
public class Garbage{
 public static void main(String []args){
  int i;
  i=1;
  if(i==1)
  {
   MyClass myClass=new MyClass("myClass");
  }
  System.gc();
 }
}
这个例子和上面那个例子基本一样,只是在主函数中做了一点改变,它的输出是什么呢?你应该能猜到:
myClass is created.
finalize myClass
可能你要问:为什么myClass要被回收呢?不是说Garbage Collection只回收匿名对象吗?是的,没错。但你别忘了,myClass的定义是在一个if快之中的,我们加一个变量i,令它等于1,只是为了执行if快中的语句,当if快结束后,myClass的作用域就结束了,所以在if快后面的语句中不能出现myClass对象,因为它已经被消除了,但是myClass只是一个引用,引用被消除并不是意味着该引用所指的对象也别消除,所以当if快执行完之后,myClass所指向的内存区域就称为无用的内存了,所以当再执行System.gc()时,JVM自然会回收myClass指向的内存。
接下来我们谈一谈finalize()函数的使用。实际上,Garbage Collection在回收内存的过程中finalize()方法并没有什么用,它只是在内存回收之前被调用,那么我们在finalize()中应该写一些什么呢?
其实,一般情况下,我们用不着改写finalize()。我们从java的回收机制来阐述finalize()的用处。Garbage Collection回收的内存必须是通过new操作符分配的,如果不是通过new分配的,那Garbage Collection是没法回收的。既然如此,finalize()函数就是用来处理不是通过new分配的内存,也就是说不是以对象形式存在的内存。但是我们又知道,Everything is object in Java(所有的东西在java中都被视为对象)。这么看来finalize()是不是根本没用呢?其实不然,有时候我们会使用一个特殊的机制来进行java编程(如使用JNI),这种机制汇集了java以外的其他预言(如C,C++),在这种情况下,由C语言中malloc()分配的内存就不能java中的Garbage Collection来回收,此时我们要在finalize()方法中写进一些代码来处理这种内存占用。所以说,finalize()方法你可能永远也用不着(除了上述情况)。

欢迎与我讨论

友好的翼

2006 1 1

E-Mail: youhaodeyi@gmail.com

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值