当前位置:

首页 > 编程开发 > Java之对象销毁和finalize方法如何使用

Java之对象销毁和finalize方法如何使用

对象的销毁在C++中析构方法用于释放资源并且销毁对象本身。在Java中,由于GC的存在,我们不需要手动回收内存,这大大减少了工作量,也提高了程序的安全性。但是Java也确实存在一个类似于C++中析构的函数。finalize方法重载该方法,用于在类被GC回收的时候执行一些操作。下面是一个类实现finalize的示例。Aoo类具有一个int一个String属性,重载了toString并且在构造其中打印这个对象及其创建时间,在finalize中打印这个对象及调用时间。Aoo类publicclassAoo{pri

本文目录

    Java之对象销毁和finalize方法如何使用

    对象的销毁

    在C++中析构方法用于释放资源并且销毁对象本身。

    在Java中,由于GC的存在,我们不需要手动回收内存,这大大减少了工作量,也提高了程序的安全性。但是Java也确实存在一个类似于C++中析构的函数。

    finalize方法

    重载该方法,用于在类被GC回收的时候执行一些操作。

    下面是一个类实现finalize的示例。

    Aoo类具有一个int 一个String属性,重载了toString并且在构造其中打印这个对象及其创建时间,在finalize中打印这个对象及调用时间。

    Aoo类

    public class Aoo {
           private int id;
           private String name;
    
           public Aoo(){
                  this(0, null);
           }
    
           public Aoo(int id, String name){
                  this.id = id;
                  this.name = name;
                  System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
           }
            /*
            * 省略get/set/toString
            */
           protected void finalize() throws Throwable{
                  super.finalize();
                  System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
           }
    }

    首先,一个简单的测试

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  Aoo a = new Aoo(1, "a");
                  a = null;
                  System.gc()
                  Thread.sleep(2000);
                  System.exit(0);
           }
    }

    打印结果:

    id:1 name:a now create:1497547723036
    id:1 name:anow finalize:1497547724059

    GC对对象的回收

    这里手动调用了GC来清理内存,而如果将其注释掉System.gc();,打印结果是这样的:

    id:1 name:a now create:1497547846923

    也就是说,在没有特意调用GC的情况下,finalize方法根本没有被调用,也就是说这个对象根本没有被主动回收。

    和想象中的不同,GC的运行方式是惰性的,也就是说,在内存没有一处的情况下,GC不会去主动回收对象,为了验证这个想法,我创建了一个线程,用于不断的消耗内存,并且不主动调用GC。

    ThreadA类

    public class ThreadA implements Runnable{
           public void run() {
                  List list = new ArrayList();
                  int i = 0;
                  while(true){
                         list.add(i);
                         i++;
                  }
           }
    }

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  Aoo a = new Aoo(1, "a");
                  a = null;
                  ThreadA ta = new ThreadA();
                  Thread t = new Thread(ta);
                  t.start();
                  Thread.sleep(2000);
                  System.exit(0);
           }
    }

    打印结果:

    id:1 name:a now create:1497548135268
    id:1 name:anow finalize:1497548135386

    这一次尽管没有手动调用GC,但是finalize方法仍然运行了,也就是说,只有在内存被消耗、需要GC出面清理内存的时候,GC才会运行。

    这样的finalize方法确实不靠谱,连能不能被调用都不一定,更不用说完成什么特定的操作了,如果需要关流等回收资源,不如手动调用一个方法,或者在final块中统一释放资源。

    在finalize方法中,是否重新给自己指定一个引用来避免被GC回收?

    尝试在finalize方法中重新引用来让GC无法回收

    修改后的Aoo如下

    public class Aoo {
           public static Aoo SAVE = null;
           private int id;
           private String name;
    
           public Aoo(){
                  this(0, null);
           }
    
           public Aoo(int id, String name){
                  this.id = id;
                  this.name = name;
                  System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
           }  
           /*
            * 省略get/set/toString
            */
           protected void finalize() throws Throwable{
                  super.finalize();
                  System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
                  SAVE = this;
           }
    }

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  Aoo.SAVE = new Aoo(1, "a");
                  Aoo.SAVE = null;
                  System.gc();
                  Thread.sleep(500);
                  System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );              
                  System.exit(0);             
           }
    }

    打印结果:

    id:1 name:a now create:1497551409195
    id:1 name:anow finalize:1497551409201
    a is alive

    这里看出,Aoo.SAVE对象确实“复活了”,不过这样的操作是有限制的,如果故技重施不会再一次“复活”该对象。

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  Aoo.SAVE = new Aoo(1, "a");
                  Aoo.SAVE = null;
                  System.gc();
                  Thread.sleep(500);
                  System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );
    
                  Aoo.SAVE = null;
                  System.gc();
                  Thread.sleep(500);
                  System.out.println(Aoo.SAVE == null? "a is dead" : "a is alive" );
    
                  System.exit(0);          
           }
    }

    打印结果:

    id:1 name:a now create:1497551587715
    id:1 name:anow finalize:1497551587721
    a is alive
    a is dead

    这里注意到,两次的操作是相同的,而finalize方法只会被系统调用一次。

    如果finalze方法中出现死循环会发生什么?

    Aoo类

    public class Aoo {
           private int id;
           private String name;
    
           public Aoo(){
                  this(0, null);
           }
    
           public Aoo(int id, String name){
                  this.id = id;
                  this.name = name;
                  System.out.println(this.toString() + " now create:" + System.currentTimeMillis());
           }
           /*
            * 省略get/set/toString
            */
           protected void finalize() throws Throwable{
                  super.finalize();
                  while(true){
                         System.out.println(this.toString() + "now finalize:" + System.currentTimeMillis());
                         Thread.sleep(100);
                  }
           }
    }

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  Aoo a1 = new Aoo(1 , "a1");
                  Aoo a2 = new Aoo(2 , "a2");
                  a1 = null;
                  a2 = null;
                  ThreadA ta = new ThreadA();
                  Thread t = new Thread(ta);
                  t.start();
                  Thread.sleep(5000);
    
                  System.exit(0);
           }
    }

    打印结果:

    id:1 name:a1 now create:1497552024252
    id:2 name:a2 now create:1497552024252
    id:1 name:a1now finalize:1497552024373
    id:1 name:a1now finalize:1497552024503
    id:1 name:a1now finalize:1497552026848
    id:1 name:a1now finalize:1497552028960
    id:1 name:a1now finalize:1497552032363

    结果是随机的,有时候是执行的a1的finalize,有的时候执行的是a2的。

    这个结果说明了两点:

    1.finalze方法在的线程优先级很低,时间间隔相当的不确定并且明显大于100毫秒。

    2.这个死循环导致了别的对象的finalize方法无法执行。

    如果对象的创建出现这种死循环,会不会导致对象无法销毁进而导致内存溢出?

    我们大量创建Aoo对象,并且等待GC自己回收内存。

    为了直观的观看finalize方法的调用情况,删除掉了Aoo对象初始化的时候的打印代码。

    main方法

    public class FinalizeTest {
           public static void main(String[] args) throws Exception {
                  int i = 1;
                  while(true){
                         Aoo a = new Aoo(i , "a" + i);
                         i++;
                  }
           }
    }

    让程序执行了约两分钟,然后手动终止,查看输出

    1497554225913
    id:269614 name:a269614now finalize:1497554226151
    id:269614 name:a269614now finalize:1497554227635
    id:269614 name:a269614now finalize:1497554227735
    id:269614 name:a269614now finalize:1497554227836
    id:269614 name:a269614now finalize:1497554229586
    id:269614 name:a269614now finalize:1497554229686
    id:269614 name:a269614now finalize:1497554229951
    id:269614 name:a269614now finalize:1497554230051
    id:269614 name:a269614now finalize:1497554230152
    id:269614 name:a269614now finalize:1497554233699
    id:269614 name:a269614now finalize:1497554233800
    id:269614 name:a269614now finalize:1497554233900
    id:269614 name:a269614now finalize:1497554234308
    id:269614 name:a269614now finalize:1497554234408
    id:269614 name:a269614now finalize:1497554234508
    id:269614 name:a269614now finalize:1497554235053
    id:269614 name:a269614now finalize:1497554235153
    id:269614 name:a269614now finalize:1497554235253
    id:269614 name:a269614now finalize:1497554235823
    id:269614 name:a269614now finalize:1497554235923
    id:269614 name:a269614now finalize:1497554236023
    id:269614 name:a269614now finalize:1497554240324
    id:269614 name:a269614now finalize:1497554240424
    id:269614 name:a269614now finalize:1497554240525
    id:269614 name:a269614now finalize:1497554241146
    id:269614 name:a269614now finalize:1497554241247
    id:269614 name:a269614now finalize:1497554241347
    id:269614 name:a269614now finalize:1497554241448
    id:269614 name:a269614now finalize:1497554242020
    id:269614 name:a269614now finalize:1497554242120
    id:269614 name:a269614now finalize:1497554242220
    id:269614 name:a269614now finalize:1497554242321
    id:269614 name:a269614now finalize:1497554242421
    id:269614 name:a269614now finalize:1497554242521
    id:269614 name:a269614now finalize:1497554248367
    id:269614 name:a269614now finalize:1497554248467
    id:269614 name:a269614now finalize:1497554248567
    id:269614 name:a269614now finalize:1497554248667
    id:269614 name:a269614now finalize:1497554249534
    id:269614 name:a269614now finalize:1497554249634
    id:269614 name:a269614now finalize:1497554249734
    id:269614 name:a269614now finalize:1497554249835
    id:269614 name:a269614now finalize:1497554255954
    id:269614 name:a269614now finalize:1497554256055
    id:269614 name:a269614now finalize:1497554256155
    id:269614 name:a269614now finalize:1497554256255
    id:269614 name:a269614now finalize:1497554256356
    id:269614 name:a269614now finalize:1497554257285
    id:269614 name:a269614now finalize:1497554257386
    id:269614 name:a269614now finalize:1497554257486
    id:269614 name:a269614now finalize:1497554257586
    id:269614 name:a269614now finalize:1497554257686
    id:269614 name:a269614now finalize:1497554268652
    id:269614 name:a269614now finalize:1497554268753
    id:269614 name:a269614now finalize:1497554268853
    id:269614 name:a269614now finalize:1497554268953
    id:269614 name:a269614now finalize:1497554269054
    id:269614 name:a269614now finalize:1497554269154
    id:269614 name:a269614now finalize:1497554277474
    id:269614 name:a269614now finalize:1497554292852
    id:269614 name:a269614now finalize:1497554301062

    可以发现两个情况:

    1.只有一个对象的finalize方法被执行了,也就是说这个死循环的finalize方法阻止了其他对象执行finalize方法

    2.程序执行很快的一段时间后,finalize方法就开始执行,但是随着内存消耗的不断增加,finalize方法被执行的次数也就越来越少。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。