Java中回收资源的方法

本文探讨了Java中如何回收对象占用的内存资源,包括垃圾自动回收机制和非内存资源的回收。Java通过垃圾回收机制自动管理堆内存,而对象占用的非内存资源如数据库连接则需要通过finalize和finally机制进行手动释放。尽管finalize存在不确定性,但finally块能确保资源在异常情况下也能得到清理,类似于C++的析构函数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.回收对象占用的内存

对于内存的回收,JAVA与C++不同。在C++中,对象占用的内存,其回收是明确无误的。在栈中分配的对象,当代码的运行离开对象的作用域时,系统自动调用对象的析构函数,然后回收内存。对于在堆中为对象分配的内存,即使离开分配时的作用域,系统也不回收内存,只有开发者调用如delete这样的操作符时,系统才会调用对象的析构函数,然后回收内存。如果发开者忘记调用delete回收堆中分配的对象占用的内存,就内存泄露了。

而在JAVA中,所有对象都是在堆中分配的,并且对象没有析构函数。开发者无需关心对象占用的内存回收问题。Java实现了垃圾自动回收机制。如下面这样:

1    public static void main(String[] args) {
2        Test t = new Test();
3        t = new Test();
4        t = null;
5    }

在第二行为t创建了对象,此时新创建的对象是可达的,因为有"t"这个句柄指向它,所以这个对象不会被当成垃圾回收。
在第三行为t重新创建了对象,此时在第二行为t创建的对象就没有句柄指向它,它变成不可达的状态,这个时候第二行分配的对象就可以当成垃圾回收了。
在第四行将t放空。同理,第三行分配的对象也变成不可达,这个时候第三行创建的对象也可以回收了。

这种机制使程序员不用再关心内存的回收问题,只管创建不管回收,回收由系统负责,当然也就避免了因为程序员粗心而造成的内存泄漏。当然代价就是效率,内存的回收由系统控制,什么时候回收是不确定的。

2.回收对象占用的非内存资源

对象占用的内存资源,由系统自动回收。但对象还可能占用其它资源,如对数据库的链接,这种资源在不用的时候也一定要回收,这个垃圾回收并不负责。在C++中,这种类型资源的回收由析构函数控制,但是Java中没有析构函数,但它也有类似的机制。一种是finalize机制,另一种是finally机制。

2.1.finalize机制

finalize机制存在很多问题,如资源回收不及时,是不是真的会回收不确定等,并不是一种推荐的回收资源方法,但需要了解一下它的运行机制。

finalize是Object类中的一个方法,Java类中的所有类都是单根结果,都是由Object类派生而来。如果在派生类中实现了finalize方法,那么系统在回收对象占用的内存之前会先调用finalize方法,以释放对象占用的其它资源,然后再回收内存资源。当然,如果不为对象实现finallize方法,那么系统在对象占用的资源之前,自然也就不会调用finallize方法,直接回收就行了。

Java中的对象有两组状态,一种是可达状态,包括(reachable,finalize-reachable,unreachable)。当然如果对象没有实现finalize方法,它就不会有finalize-reachable状态,只有(reachable,unreachable)两种状态。没有实现finallize方法的对象,它的状态变化很简单,只有两种状态,当有句柄指向它时,就是reachable状态,如Test t = new Test(),这个时候不回收内存。接来来随着代码的运行,有t=null这句话,则t原来指向的对象就变成unreachable状态了,这个时候系统就可以回收内存了。如果对象实现了finallize方法,则其状态的变化要复杂一些,接下来说明。

另一组是结束状态,也就是finallize状态,前边说了,只有实现了finallize方法的对象才有这种状态。它也有三个阶段(unfinallized、finallizable、finallized)。这种类型的对象状态变化比较复杂,还涉及到对象复活的问题,通过一段代码说明:

public class GC { 
   
    public static GC SAVE_HOOK = null; 
   
    public static void main(String[] args) throws InterruptedException {
        // 新建对象,因为SAVE_HOOK指向这个对象,对象此时的状态是(reachable,unfinalized)
        SAVE_HOOK = new GC(); 
        // 将SAVE_HOOK设置成null,此时刚才创建的对象就不可达了,因为没有句柄再指向它,
        // 对象此时状态是(unreachable,unfinalized)
        SAVE_HOOK = null; 
        // 强制系统执行垃圾回收,系统发现刚才创建的对象处于unreachable状态,
        // 并检测到这个对象的类覆盖了finalize方法,因此把这个对象放入F-Queue队列,
        // 由低优先级线程执行它的finalize方法,
        // 此时对象的状态变成(finalize-reachable,finalizable)
        System.gc(); 
        // sleep,目的是给低优先级线程从F-Queue队列取出对象并执行其finalize方法提供机会。
        // 在执行完对象的finalize方法中的super.finalize()时
        // 对象的状态变成(unreachable,finalized)状态,
        // 但接下来在finalize方法中又执行了SAVE_HOOK = this;
        // 又有句柄指向这个对象,对象又可达了,也就是复活。
        // 因此对象的状态又变成了(reachable, finalized)状态。
        Thread.sleep(500);
        if (null != SAVE_HOOK) {
            // 这句话会输出,注意对象由unreachable,经过finalize复活,又变成reachable。
            System.out.println("Yes , I am still alive"); 
        } else { 
            System.out.println("No , I am dead"); 
        } 
        // 再一次将SAVE_HOOK放空,此时刚才复活的对象,状态变成(unreachable,finalized)
        SAVE_HOOK = null; 
        // 再一次强制系统回收垃圾,此时系统发现对象不可达,虽然覆盖了finalize方法,
        // 但已经执行过了,因此直接回收。
        System.gc(); 
        // 为系统回收垃圾提供机会
        Thread.sleep(500); 
        if (null != SAVE_HOOK) { 
            // 这句话不会输出,因为对象已经彻底消失了。
            System.out.println("Yes , I am still alive"); 
        } else { 
            System.out.println("No , I am dead"); 
        } 
    } 
   
    @Override 
    protected void finalize() throws Throwable { 
        super.finalize(); 
        System.out.println("execute method finalize()"); 
       // 注意这句话,让对象的状态由unreachable变成reachable,就是对象复活
        SAVE_HOOK = this; 
    } 
} 

代码参考自:https://www.cnblogs.com/Smina/p/7189427.html

2.2.finally机制

示例代码如下:

public class Circle {
	
	private int res = 0;
	
	void mallocRes(int x) {
		res = x;
		System.out.println("Malloc resource:" + res);
	}
	
	void cleanRes() {
		System.out.println("Clean resource:" + res);
		res = 0;
	}
	
	void doSomething() {
		System.out.println("Do something");
	}
	
	public static void main(String[] args) {
		Circle test = new Circle();
		try {
			test.mallocRes(5);
			test.doSomething();
			// 其它代码
		} finally {
			test.cleanRes();
		}
	}
}

首先将分配资源的方法放在try块内,本例中用test.mallocRes(5)表示,将释放资源的方法放在try块后边的finally块中。这样当代码的执行离开try块时,无论发生何种情况,比如各种异常等,finally块中的test.cleanRes()都会被执行,从而实现资源的及时释放,并且一定会被释放。Java中的finally就相当于C++中的析构函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值