java中的对象与垃圾回收

java的垃圾回收时java语言得重要功能之一。当程序创建对象、数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保存在这块内存区,当这块内存区不在被任何引用变量所引用时,这块内存就变成了垃圾,等待垃圾回收机制进行回收。

  • 垃圾回收机制只负责回收堆内存中的对象,不回收任何物理资源;
  • 程序无法精确控制垃圾回收运行,垃圾回收会在合适的时候进行。当对象永久性地失去引用后,系统会在合适的时候回收它所占的内存。
  • 在垃圾回收机制回收任何对象之前,总先会调用它的finalize()方法,该方法可能使该对象重新复活,从而呆滞垃圾回收机制取消。

对象在内存中的状态

在这里插入图片描述

package org.westos.Mydemo;

import java.lang.String

public class StatusTranfer {
    public static void test() {
        String a = new String("我爱java");//1
        
        a=new String ("java我爱你");//2
    }

    public static void main(String[] args) {
        test();
    }
}

定义了一个a变量并让它指向"我爱java"对象,该段代码执行完毕后,"我爱java"变成可达状态,当执行第2段代码后,a又指向"java我爱你"对象,第一个对象变成可恢复状态,第二个对象变成可达状态。

一个对象可以被一个方法的局部变量引用,也可以被其他类的类变量引用,或被其他对象的实例变量引用。当某个对象被其他类的类变量引用时,只有当该类被销毁后,该对象才会进入可恢复状态;当某个对象被其他对象的实力变量引用时,只有当该对象被销毁后,该对象会进入可恢复状态。

强制垃圾回收

当一个对象失去引用后,系统何时调用finalize方法对它进行资源管理,何时它会变成不可达对象,系统何时回收它所占有的内存,对于程序完全透明。程序只能控制一个对象何时不再被任何引用变量引用,决不能控制它何时被回收。
虽然系统不能控制时机,但依然可以强制系统进行垃圾回收,其实这种机制只是通知系统进行垃圾回收,但系统是否进行回收依然不确定。
两种回收方式:

  • 调用System类的ge静态方法:System.gc();
  • 调用Runtime对象的ge实例方法:Runtime.getRuntime().gc();
package org.westos.Mydemo;

public class gcdemo {

    public static void main(String[] args) {
        for (int i = 0; i <4 ; i++) {
            new gcdemo();
//            System.gc();
            Runtime.getRuntime().gc();
        }
    }

    public void finalize(){
        System.out.println("系统正在清理gcdemo对象的资源...");
    }
}

在这里插入图片描述

这种方式只是程序建议系统回收垃圾,系统完全有可能不会立即回收,垃圾回收机制会在收到通知后尽快进行处理。

finalize方法

java默认使用finalize方法来清理资源。
方法原型为:protected void finalize() throws Throwable
当这个方法执行后,对象消失,垃圾回收机制开始执行。throws Throwable表示它可以抛出任何类型的异常。
如果程序在终止之前始终没有进行垃圾回收,则不会调用失去引用对象的finalize方法来清理资源。只有当程序认为需要更多的额外内存时,垃圾回收机制才会进行垃圾回收。

finalize方法的特点:

  • 永远不要主动调用某个对象的finalize方法,该方法应该交给垃圾回收机制调用;
  • finalize方法的调用具有不确定性;
  • 当JVM执行可恢复对象的finalize方法时,可能使该对象或系统中的其他对象重新变成可达状态;
  • 当JVM执行finalize方法时出现异常时,垃圾回收机制不会报告异常;

演示如何在finalize方法里复活自身:

package org.westos.Mydemo;

public class FinalizrTest {
    private static FinalizrTest ft=null;
    public void info(){
        System.out.println("测试资源管理的finalize方法");
    }

    public static void main(String[] args) {
        //创建FinalizrTest对象立即进入可恢复状态、
        new FinalizrTest();
        //通知系统进行资源回收
        System.gc();//1
        //垃圾回收机制调用可恢复对象的finalize方法
//        Runtime.getRuntime().runFinalization();//2
        System.runFinalization();//3
        ft.info();
    }
    public void finalize(){
        //让ft引用到试图回收可恢复对象
        ft=this;
    }
}

上面代码重写了finalize方法,让对象在方法中复活;如果调用了2、3代码可以看到下列运行结果,也就是说finalize方法立即被执行了,对象随之复活正常调用info方法;

在这里插入图片描述

如果将2,3代码注释掉,会发现有时候能调用到info方法,有时候会出现下列的空指针异常现象,这就是垃圾回收机制并没有立即回收对象的结果,也就是没有及时调用finalize方法
在这里插入图片描述

对象的软、弱和虚引用

对大部分对象而言,程序里会有一个引用变量引用该对象,这是最常见的方式。

java中有四种引用方式:

  • 强引用(StrongReference):这种方式是最常见的,程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象。
  • 软引用(SoftReference):需要通过SoftReference类来实现,当一个对象只有软引用时,他有可能被垃圾回收机制回收,对于只有软引用的对象而言,当系统内存空间足够时,他不会被回收,程序也可使用该对象,当空间不足时,系统可能会回收它;
  • 弱引用(WeakReference):通过WeakReference类实现,弱引用和软引用很像,但是它的级别更低。当系统垃圾回收机制运行时,不管系统内存是否足够,总会被回收。
  • 虚引用(PhantomReference):通过PhantomReference类来实现,类似于没有引用,主要用于跟踪对象被垃圾回收的状态,不能单独使用,必须和引用队列联合使用。

引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收后对象的引用。

package org.westos.demo3;

import java.lang.String;
import java.lang.ref.WeakReference;

public class ReferenceTest {
    public static void main(String[] args) {
        //创建一个字符对象
        String str = new String("我爱java");

        //创建一个弱引用并且引用到字符串
        WeakReference wr = new WeakReference(str);
        //切断引用
        str=null;
        //取出弱引用所引用的对象,虽然已经切断了引用,但是内存空间不紧张,索引对象未被回收
        System.out.println(wr.get());//我爱java
        //强制垃圾回收
        System.gc();
        System.runFinalization();
        //再次取出弱引用所引用的对象,由于已经对象已经被回收,所以输出null
        System.out.println(wr.get());//null

    }
}

用途

  • 可以避免在程序执行期间将对象留在内存中;
  • 如果以软引用、弱引用、虚引用来引用对象,垃圾回收器就能够碎一地释放对象,如果希望尽可能减小程序在其声明周期中所占用的内存大小时,这些引用类就很有用。

猜你喜欢

转载自blog.csdn.net/mashaokang1314/article/details/83793075