代码改变世界

C#’s ~ vs Java’s finalize

2011-05-14 10:50  横刀天笑  阅读(1542)  评论(7编辑  收藏  举报

本文是上周写一个功能测试时,期望在测试结束时,利用Java的析构函数来关闭浏览器发现的问题,当然,这种方式是不建议采用的。因为我的背景是.NET开发,因此对Java的析构函数产生了误解。

作为一个C# Programer我们都知道,垃圾回收会帮我们回收托管资源,但那些非托管资源(比如文件句柄,数据库连接)垃圾回收是不会帮我们回收的,所以我们必须显式的回收这些资源。比如很多使用了非托管资源的类型都实现了Dispose方法,他们期望使用这些类型的开发人员能够显式的去调用这个方法来释放非托管资源,不过如果你没有显式调用那不就产生资源泄露了?别怕,还有最后的安全屏障:终结方法(不过终结方法会带来其他很多问题,这个在《CLR via C#》里有很详细的描述,本文关注点并不在此)。比如下面这段代码:

using System;
 
public class Wrapper
{
    public Wrapper()
    {
        Console.WriteLine("ctor");
    }
 
    ~Wrapper()
    {
        Console.WriteLine("finalize");
    }
}
 
public class Program
{
    private static Wrapper wrapper = null;
 
    public static void Main(String[] args)
    {
        wrapper = new Wrapper();
    }
}

上面的代码执行的结果会是:

ctor
finalize

C#的终结方法(析构函数,为了避免产生误解,微软后来改名了)在对象被垃圾回收或程序退出时会被调用。

再看看下面这段几乎一模一样的Java代码:

//Wrapper.java
public class Wrapper{
 
    public Wrapper(){
        System.out.println("ctor");
    }
    
    protected void finalize(){
        System.out.println("finalize");
    }
}
 
//Program.java
public class Program{
    private static Wrapper wrapper = null;
    
    public static void main(String[] args){
        wrapper = new Wrapper();
    }
}

输出的结果却是:

ctor

也就是析构函数并没有执行。

最后查文档得知,在JVM中如果该对象没有被垃圾回收则该对象的析构函数就不会调用。也就是说,如果你这个对象在JVM退出之前一直都没有被垃圾回收,那么默认情况下,你的析构函数是不会调用的,这也就带来了上面所说的结果。

也就是这一点让我这个.NET背景的程序员栽跟头了。

不过Java也提供了其他方式,让你确定在JVM退出时调用所有对象的析构函数,比如上面的代码只需要加一个System.runFinalizersOnExit(true),然后执行结果就和C#版本的一样了:

//Wrapper.java
public class Wrapper{
 
    public Wrapper(){
        System.out.println("ctor");
    }
    
    protected void finalize(){
        System.out.println("finalize");
    }
}
 
//Program.java
public class Program{
    private static Wrapper wrapper = null;
    
    public static void main(String[] args){
        System.runFinalizersOnExit(true);
        wrapper = new Wrapper();
    }
}

不过System.runFinzalizersOnExit是一个已不建议采用的方法了。