Java 对象与垃圾回收

以下内容部分来自 疯狂Java讲义/李刚 ,博客仅用于个人笔记及帮助他人用途,无任何商业目的,如有侵权,请联系作者删除。

垃圾回收机制特征

垃圾回收机制具有以下特征
(1) 垃圾回收机制只负责回收内存中的对象,不会回收任何物理资源(例如数据库连接、网络IO等资源);
(2) 程序无法精确控制垃圾回收的运行,垃圾回收会在合适的时候进行。当对象永久地失去引用后,系统会在合适的时候回收它所占的内存。
(3) 在垃圾回收机制回收任何对象之前,总会调用它的 finalize() 方法,该方法可能使该对象重新复活(让一个引用变量重新引用该对象),从而导致垃圾回收机制取消回收。

对象在内存中的状态

当一个对象在堆内存中运行时,根据他被引用变量所引用的状态,可以把它所处状态分为如下三种:
(1) 可达状态:当一个对象被创建后,若有一个以上的引用变量引用它,则这个对象在程序中处于可达状态,程序可通过引用变量来调用该对象的 FIeld 和方法。
(2) 可恢复状态:如果程序中某个对象不再有任何引用变量引用它,她就进入了可恢复状态。在这种状态下,系统的垃圾回收机制准备回收该对象所占用的内存,在回收该对象之前,系统会调用所有可恢复状态的 finalize() 方法进行资源清理。如果系统在调用 finalize () 方法时重新让一个引用变量引用该对象,则这个对象会再次变为可达状态;否则该对象将进入不可达状态。
(3) 不可达状态:当对象与所有引用变量的关联都被切断,且系统已经调用所有对象的 finalize() 方法后依然没有使该对象变成可达状态,那么这个对象将永久性失去引用,最后变成不可达状态。只有当一个对象处于不可达状态时,系统才会真正回收该对象所占有的资源。

强制垃圾回收

当一个对象失去引用后,系统何时调用它的 finalize() 方法对它的资源进行清理,何时它会变成不可达状态,系统何时回收他所占用的内存,对于程序完全透明。程序只能控制一个对象何时不再被任何引用变量引用,绝不能控制它何时被收回。
程序无法精确控制 Java 垃圾回收的时机,但我们仍然可以强制系统进行垃圾回收——这种强制只是会通知系统进行垃圾回收,但系统是否进行垃圾回收仍然不能确定。大部分时候,程序强制系统垃圾回收后总会有一些效果。强制系统回收垃圾有如下两个方法:
(1) 调用 System 类的 gc() 静态方法:System.gc();
(2) 调用 Runtime() 对象的 gc() 实例方法:Runtime.getRuntime().gc();

例:

public class GcTest{
  public static void main(String[] args){
    for(int i=0;i<4;i++){
      new GcTest();
    }
  }
  public void finalize(){
    System.out.println("系统正在清理GcTest资源。。。");
  }
}

运行之后没有任何输出,说明系统未调用 GcTest 对象的 finalize() 方法。但如果将程序改成如下形式:

public class GcTest{
  public static void main(String[] args){
    for(int i=0;i<4;i++){
      new GcTest();
      Runtime.getRuntime().gc();
    }
  }
  public void finalize(){
    System.out.println("系统正在清理GcTest资源。。。");
  }
}

编译后使用 java -verbose:gc GcTest 命令来运行,其中指定 -verbose:gc 选项,可以看到每次垃圾回收后的提示信息。
运行结果如下:
运行结果

可以看出每次调用Runtime.getRuntime().gc() 代码后还是有些动作的,可以看出垃圾回收前、回收后的内存占比。
虽然上面程序显示了程序强制垃圾回收后的效果,但这种强制只是建议系统立即进行垃圾回收,系统完全有可能并不立即进行垃圾回收,垃圾回收机制也不会对程序的建议完全置之不理:垃圾回收机制会在收到通知后,尽快进行垃圾回收。

finalize 方法

在垃圾回收机制回收某个对象所占用的内存之前,通常要求程序调用适当的方法来清理资源,在没有明确指定清理资源的情况下,Java 提供了默认机制来清理该对象的资源,这个机制就是 finalize() 方法。该方法是定义在 Object 类里的实例方法、方法原型为:

protected void finalize() throws Throwable

当 finalize() 方法返回后,对象消失,垃圾回收机制开始执行。方法原型中的 throws Throwable 表示它可以抛出任何类型的异常。

任何 Java 类都可以重写 Object 类的 finalize() 方法,在该方法中清理该对象占用的资源。如果程序终止之前始终没有进行垃圾回收,则不会调用失去引用对象的 finalize() 方法来清理资源。垃圾回收机制何时调用对象的 finalize() 方法完全是透明的,只有当程序认为需要更多的额外内存时,垃圾回收机制才会进行垃圾回收。因此,完全有可能出现这样一种情形:某个失去引用的对象只占用了少量内存,而且系统没有产生严重的内存需求,因此垃圾回收机制没有试图回收该对象所占用的资源,所以该对象的 finalize() 方法也不会得到调用。

finalize 方法具有如下 4 个特点:
(1) 永远不要主动调用某个对象的 finalize 方法,该方法应该交给垃圾回收机制调用;
(2) finalize 方法何时被调用,是否被调用具有不确定性,不要把 finalize 方法当成一定会被执行的方法;
(3) 当 JVM 执行可恢复对象的 finalize 方法时,可能使该对象或系统中其他对象重新变成可达状态。
(4) 当 JVM 执行 finalize 方法出现异常时,垃圾回收机制不会报告异常,程序继续运行。
下面程序演示了如何在 finalize 方法里复活自身,并通过该程序看出垃圾回收的不确定性。

public class FinalizeTest{
  private static FinalizeTest ft = null;
  public void info(){
    System.out.println("测试资源清理的 fianlize 方法...");
  }
  public static void main(String[] args) throws Exception{
    new FinalizeTest();
    System.gc();
    //Thread.sleep(2000);
    //强制垃圾回收机制调用可恢复对象的 finalize 方法
    Runtime.getRuntime().runFinalization();
    ft.info();
  }
  public void finalize(){
    ft=this;
  }
}

猜你喜欢

转载自blog.csdn.net/u013872857/article/details/50570284