zoukankan      html  css  js  c++  java
  • 5.10 对象与垃圾回收


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

    一、对象在内存中的状态

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

    例如:下面创建了两个字符串对象,并创建了一个引用变量依次指向两个对象。

    public class StatusTranfer
    {
    	public static void test()
    	{
    		var a=new String("轻量级JAVA EE企业应用开发");//①
    		a=new String("疯狂Java讲义");//②
    	}
    	public static void main(String[] args)
    	{
    		test();//③
    	}
    }
    

    当程序执行test()方法的①代码时,代码定义了一个a变量,并让该变量指向"轻量级JAVA EE企业应用开发"的字符串,改代码结束后,"轻量级JAVA EE企业应用开发"字符串对象变成可达状态。
    当程序执行②代码时,代码再次创建"疯狂Java讲义"字符串对象,并让a变量指向该对象,此时"轻量级JAVA EE企业应用开发"处于可恢复状态;而"疯狂Java讲义"处于不可状态。
    一个对象被另一个方法的局部变量引用时,也可以被其他类的类变量引用或被其他对象的实例变量引用。当某个对象被其它类的类变量引用时,只有当该类被销毁后,该对象才会进入可恢复状态;当某个对象被其它类的实例变量引用时,只有当该对象被销毁后,该对象才会进入可恢复状态。

    二、强制垃圾回收

    系统无法精确控制Java垃圾回收的时机,但依然可以强制系统进行垃圾回收——这种强制只是通知系统进行垃圾回收,但系统是否进行垃圾回收依然不能确定。
    强制系统垃圾回收有两种方式:
    1、调用System类的静态方法gc():System.gc()
    2、调用Runtime对象的gc()实例方法。
    下面程序创建了四个匿名对象,每个对象创建完成后进入可恢复状态,等待系统回收,知道程序退出,系统依然不会回收该资源。

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

    编译会出现下面提示:
    ---------- 编译Java ----------
    注: GcTest.java使用或覆盖了已过时的 API。
    注: 有关详细信息, 请使用 -Xlint:deprecation 重新编译。
    因为系统何时调用对象的finalize()不确定,因此Java 开始该方法标记为不推荐。
    但如果程序改成如下格式:

    public class GcTest
    {
    	public static void main(String[] args)
    	{
    		for(var i=0;i<4;i++)
    		{
    			new GcTest();
    			//下面两种用法作业相同,强制系统进行垃圾回收
    			//System.gc();
    			Runtime.getRuntime().gc();
    		}
    	}
    	public void finalize()
    	{
    		System.out.println("系统正在清理GcTest对象的资源");
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    系统正在清理GcTest对象的资源
    系统正在清理GcTest对象的资源
    系统正在清理GcTest对象的资源
    
    输出完成 (耗时 0 秒) - 正常终止
    

    三、finalize()方法

    在没有明确指明清理垃圾资源的情况下,Java提供了默认机制来清理该对象的资源,这个机制就是finalize()方法。该方法定义在Object类里的实例方法,其原型为:
    protected void finalize() throw Throwable
    当finalize()方法返回后,对象消失,垃圾回收机制开始执行。方法原型中throw Throwable表示它可以抛出任何类型的异常。
    Java任何类都可以重写Object类的finalize()方法,在该方法中清理该对象所占用的资源。
    finalize方法的四个特点:
    1、永远不要主动调用某个对象的finalize方法,该方法应该交由系统的垃圾回收机制调用。
    2、finalize()方法何时被调用,是否被调用具有不确定性,不要把finalize()方法当成一定会执行的方法。
    3、当JVM执行可恢复对象的finalize()方法时,可能是该对象或则系统中的其他对象重新变成可达状态。
    4、当JVM调用finalize()方法出现异常时,垃圾回收机制不会报告异常,程序继续执行。

    四、对象的软、弱和虚引用

    java.lang.ref包下提供三个类:SoftReference、PhantomReference和WeakReference,它们分别代表了系统对三种对象的引用方式:软引用、虚引用和弱引用。
    因此Java语言对象的引用有四种方式:
    1、强制引用(StrongReference)
    最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象,对象这数组都是这种引用方式。当一个对象被一个或多个引用引用时,它处于可达状态不可能被GC回收。
    2、软引用(SoftReferece)
    软引用通过SoftReferece类来实现,当一个对象只有弱引用时,它可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统空间足够大时,他不会被系统回收,程序也可使用该对象;当系统空间不足时,系统可能会回收它。软引用通常用于堆内存敏感的程序中。
    3、弱引用(WeakReference)
    弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用级别更低。对于只有弱引用的对象而言,当系统的垃圾回收机制运行时,不管系统的内存是否足够,总是回收该对象所占的内存。(当然需要等到垃圾回收机制运行时)
    4、虚引用(PhantomReference)
    虚引用完全等同于没有引用。甚至对象不能感受到虚引用的存在,如果一个对象只有虚引用时,那么它和没有引用的效果大致相同。虚引用用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合引用。
    引用队列由java.lang.ref.ReferenceQueue类表示,它用于保存被回收后对象的引用。
    (1)当联合使用软引用、弱引用、引用队列时,系统在回收被引用的对象之后,将把被回收对象的引用添加到关联的引用队列中。
    (2)与软引用可弱引用不同的是,虚引用在释放之前,就把它对应的虚引用添加到关联的引用队列之中,这使得在对象回收之前采取行动。
    软引用和弱引用可以单独使用,但虚引用不可以单独使用,单独使用虚引用没有太大的意义。虚引用的主要作用就是跟踪对象被垃圾回收的状态,程序通过检查与虚引用关联的引用队列中是否已经包含了该虚引用,从而了解所引用的对象是否被回收。
    下面程序</font color=red>示范软引用被回收的过程

    import java.lang.ref.*;
    public class ReferenceTest
    {
    	public static void main(String[] args)
    		throws Exception
    	{
    		// 创建一个字符串对象
    		var str = new String("疯狂Java讲义");//不要使用String str="疯狂Java讲义",系统会使用常量管理池来使用它
    		// 创建一个弱引用,让此弱引用引用到"疯狂Java讲义"字符串
    		var wr = new WeakReference(str);  // ①
    		// 切断str引用和"疯狂Java讲义"字符串之间的引用
    		str = null;   // ②
    		// 取出弱引用所引用的对象
    		System.out.println(wr.get());  // ③
    		// 强制垃圾回收
    		System.gc();
    		System.runFinalization();
    		// 再次取出弱引用所引用的对象
    		System.out.println(wr.get());  // ④
    	}
    }
    ---------- 运行Java捕获输出窗 ----------
    疯狂Java讲义
    null
    
    输出完成 (耗时 0 秒) - 正常终止
    

    uploading-image-118730.png
    程序调用System.gc()和System.runFinalization();通知系统进行垃圾回收,wr是弱引用,不管内存是否够用。弱引用wr指向的对象都将会被回收。因此最后输出wr指向的对象将得到null。
    下面程序示范</font color=red>虚引用和引用堆栈的应用

    import java.lang.ref.*;
    public class PhantomReferenceTest
    {
    	public static void main(String[] args)
    		throws Exception
    	{
    		//创建一个字符串对象
    		var str=new String("疯狂Java讲义");
    		//创建一个引用队列
    		var rq=new ReferenceQueue();
    		//创建一个虚引用引到"疯狂Java讲义"
    		var pr=new PhantomReference(str,rq);
    		//切断str与"疯狂Java讲义"之间的连接
    		str=null;
    		//取出虚引用所引用的对象,并不能通过虚引用获取被引用的对象
    		System.out.println(pr.get());//①
    		System.gc();
    		System.runFinalization();
    		//垃圾回收之后,虚引用将放到引用队列中
    		//取出引用队列中的最先引入的引用与pr进行比较
    		System.out.println(rq.poll()==pr);//②
    
    		}
    }
    ---------- 运行Java捕获输出窗 ----------
    null
    true
    
    输出完成 (耗时 0 秒) - 正常终止
    
  • 相关阅读:
    PHP Socket 编程详解
    PHPWord生成word实现table合并(colspan和rowspan)
    PhpExcel中文帮助手册|PhpExcel使用方法
    js限制input标签中只能输入中文
    如何巧用.htaccess设置网站的压缩与缓存
    Linux xargs命令
    PHP加密解密类
    2014 年10个最佳的PHP图像操作库
    学习swoft的第二天_注解
    学习swoft的第一天
  • 原文地址:https://www.cnblogs.com/weststar/p/12449076.html
Copyright © 2011-2022 走看看