欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java的finalize方法解讀

 更新時(shí)間:2023年09月28日 09:14:57   作者:weixin_43831204  
這篇文章主要介紹了java的finalize方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

我們通常用構(gòu)造器來創(chuàng)建對(duì)象,而Finalize正好相反,構(gòu)造方法執(zhí)行對(duì)象的初始化操作,finalize方法執(zhí)行對(duì)象的銷毀操作.那我們什么時(shí)候需要使用finalize方法呢,我們都知道Java里垃圾回收器可以回收對(duì)象使用的內(nèi)存空間,但是對(duì)象可能會(huì)持有很多資源比如Socket、文件句柄等,垃圾收集器無法回收這些資源,因此你需要使用finalize方法幫助GC回收這些資源,比如關(guān)閉打開的文件或者網(wǎng)元資源,刪除臨時(shí)文件等.

一個(gè)例子

Object類是所有類的父類,如果你去查看java.lang.Object類的源碼,你會(huì)發(fā)現(xiàn)里面有個(gè)finalize方法,這個(gè)方法沒有默認(rèn)實(shí)現(xiàn),需要子類根據(jù)實(shí)際情況重寫這個(gè)方法,但是如果不恰當(dāng)使用finalize方法可能會(huì)造成很大的負(fù)面影響,

比如下面的例子:

public class Finalizer {
    @Override
    protected void finalize() throws Throwable {
    while (true) {
           Thread.yield();
      }
  }
public static void main(String str[]) {
  while (true) {
        for (int i = 0; i < 100000; i++) {
            Finalizer force = new Finalizer();
        }
   }
 }
}

當(dāng)我們運(yùn)行上述代碼時(shí),可以看到創(chuàng)建大量的Finalizer對(duì)象,運(yùn)行一段時(shí)間后一般出現(xiàn)以下兩種結(jié)果:

  • JVM異常退出并且生成了內(nèi)存鏡像Dump
  • JVM拋出了一個(gè)異常:Out of Memory:GC OverHead limit exceeded.

不管上述兩種情況,JVM都崩潰了,那到底執(zhí)行finalize方法時(shí)發(fā)生了什么.Jvm會(huì)給每個(gè)實(shí)現(xiàn)了finalize方法的實(shí)例創(chuàng)建一個(gè)監(jiān)聽,這個(gè)稱為Finalizer,每次調(diào)用對(duì)象的finalize方法時(shí),JVM會(huì)創(chuàng)建一個(gè) java.lang.ref.Finalizer 對(duì)象,這個(gè)Finalizer對(duì)象會(huì)持有這個(gè)對(duì)象的引用,由于這些對(duì)象被Finilizer對(duì)象引用了,當(dāng)對(duì)象數(shù)量較多時(shí),就會(huì)導(dǎo)致Eden區(qū)空間滿了,經(jīng)歷多次youngGC后可能對(duì)象就進(jìn)入到老年代了. java.lang.ref.Finalizer 類繼承自 java.lang.ref.FinalReference ,也是Refence的一種,因此Finalizer類里也有一個(gè)引用隊(duì)列,這個(gè)引用隊(duì)列是JVM和垃圾回收器打交道的唯一途徑,當(dāng)垃圾回收器需要回收該對(duì)象時(shí),會(huì)把該對(duì)象放到引用隊(duì)列中,這樣java.lang.ref.Finalizer類就可以從隊(duì)列中取出該對(duì)象,執(zhí)行對(duì)象的finalize方法,并清除和該對(duì)象的引用關(guān)系.需要注意的是只有finalize方法實(shí)現(xiàn)不為空時(shí)JVM才會(huì)執(zhí)行上述操作,JVM在類的加載過程中會(huì)標(biāo)記該類是否為finalize類.

GC怎么處理這些對(duì)象呢

當(dāng)老年代空間達(dá)到了OldGC條件時(shí),JVM執(zhí)行一次OldGC,當(dāng)OldGC執(zhí)行后JVM檢測(cè)到這些對(duì)象只被Finalizer對(duì)象引用,這些對(duì)象會(huì)被標(biāo)記成要被清除的對(duì)象,GC會(huì)把所有的Finalizer對(duì)象放入到一個(gè)引用隊(duì)列: java.lang.ref.Finalizer.ReferenceQueue .

Finalizer對(duì)象怎么被清理的呢

JVM默認(rèn)會(huì)創(chuàng)建一個(gè)finalizer線程來處理Finalizer對(duì)象,如果你去抓取線程堆棧的話可以看到這個(gè)線程的堆棧,

如下所示:

"Finalizer" daemon prio=10 tid=0x0962d000 nid=0x4836 runnable [0xafaa8000]
   java.lang.Thread.State: RUNNABLE
        at java.lang.Thread.yield(Native Method)
        at finalizer.finalize(finalizer.java:5)
        at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)
        at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:83)
        at java.lang.ref.Finalizer.access$100(Finalizer.java:14)
        at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:160)

這個(gè)線程唯一的職責(zé)就是不斷的從 java.lang.ref.Finalizer.ReferenceQueue 隊(duì)列中取對(duì)象,當(dāng)一個(gè)對(duì)象進(jìn)入到隊(duì)列中,finalizer線程就執(zhí)行對(duì)象的finalize方法并且把對(duì)象從隊(duì)列中刪除,因此在下一次GC周期中可以看到這個(gè)對(duì)象和Finalizer對(duì)象都被清除了.

大部分場(chǎng)景finalizer線程清理finalizer隊(duì)列是比較快的,但是一旦你在finalize方法里執(zhí)行一些耗時(shí)的操作,可能導(dǎo)致內(nèi)存無法及時(shí)釋放進(jìn)而導(dǎo)致內(nèi)存溢出的錯(cuò)誤,在實(shí)際場(chǎng)景還是推薦盡量少用finalize方法.

簡(jiǎn)單粗暴,一個(gè)死循環(huán)去隊(duì)列里面拿出Finalizer對(duì)象,并執(zhí)行finalize方法,再置空為null,可供垃圾回收

實(shí)戰(zhàn)案例

public class Finalizer {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("finalize");
    }
    public static void main(String str[]) throws IOException {
            for (int i = 0; i < 10000; i++) {
                Finalizer force = new Finalizer();
            }
            //讓線程阻塞住,方便分析內(nèi)存使用情況
        System.in.read();
    }
}

執(zhí)行main方法后使用jmap命令查看內(nèi)存使用情況,可以看到 java.lang.ref.Finalizer 和Finalizer的實(shí)例都創(chuàng)建了10000個(gè):

$ jmap -histo 8700|head -n 10
 num     #instances         #bytes  class name
----------------------------------------------
   1:           646        3398408  [I
   2:          1851        1511144  [B
   3:          6081         808864  [C
   4:         10175         407000  java.lang.ref.Finalizer
   5:         10000         160000  Finalizer
   6:          4328         103872  java.lang.String
   7:           601          64208  java.lang.Class
   8:           683          40952  [Ljava.lang.Object;
   9:           785          31400  java.util.TreeMap$Entry
  10:           248          14144  [Ljava.lang.String;

接下來使用jmap -histo:live 8700|head -n 10命令強(qiáng)制觸發(fā)一次GC,結(jié)果和前面的分析一致,F(xiàn)inalizer對(duì)象都放到引用隊(duì)列中,并依次調(diào)用了對(duì)象的finalize方法,內(nèi)存中java.lang.ref.Finalizer和Finalizer對(duì)象依然存在,不過這一java.lang.ref.Finalizer

不再引用Finalizer對(duì)象,下一次GC周期時(shí)兩者都屬于垃圾對(duì)象:

$ jmap -histo:live 8700|head -n 10
 num     #instances         #bytes  class name
----------------------------------------------
   1:         10175         407000  java.lang.ref.Finalizer
   2:          3043         372608  [C
   3:           605         273624  [B
   4:         10000         160000  Finalizer
   5:          2883          69192  java.lang.String
   6:           601          64208  java.lang.Class
   7:           631          37008  [Ljava.lang.Object;

再觸發(fā)一次jmap -histo:live 8700|head -n 10,可以看到兩者都被回收了:

$ jmap -histo:live 8700|head -n 10
 num     #instances         #bytes  class name
----------------------------------------------
   1:          3059         373224  [C
   2:           498         138064  [B
   3:          2899          69576  java.lang.String
   4:           602          64312  java.lang.Class
   5:           631          37008  [Ljava.lang.Object;
   6:           785          31400  java.util.TreeMap$Entry
   7:           227          11256  [Ljava.lang.String;

我們來總結(jié)一下

finalize對(duì)象至少經(jīng)歷兩次GC才能被回收,因?yàn)橹挥性贔inalizerThread執(zhí)行完了finalize對(duì)象的finalize方法的情況下才有可能被下次GC回收,而有可能期間已經(jīng)經(jīng)歷過多次GC了,但是一直還沒執(zhí)行finalize對(duì)象的finalize方法;

CPU資源不足的場(chǎng)景FinalizerThread線程可能因?yàn)閮?yōu)先級(jí)較低而一直沒有執(zhí)行對(duì)象的finalize方法,可能導(dǎo)致大部分對(duì)象進(jìn)入到老年代,進(jìn)而觸發(fā)老年代GC,設(shè)置觸發(fā)Full GC.

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • SpringBoot+MinIO+KKFileView實(shí)現(xiàn)文件預(yù)覽功能

    SpringBoot+MinIO+KKFileView實(shí)現(xiàn)文件預(yù)覽功能

    本文主要介紹了使用SpringBoot、MinIO和KKFileView實(shí)現(xiàn)文件上傳和在線預(yù)覽功能,通過配置MinIO存儲(chǔ)文件,并使用KKFileView生成預(yù)覽鏈接,感興趣的可以了解一下
    2024-11-11
  • 詳解Java內(nèi)存溢出的幾種情況

    詳解Java內(nèi)存溢出的幾種情況

    這篇文章主要介紹了詳解Java內(nèi)存溢出的幾種情況,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06
  • 基于MyBatis的parameterType傳入?yún)?shù)類型

    基于MyBatis的parameterType傳入?yún)?shù)類型

    這篇文章主要介紹了基于MyBatis的parameterType傳入?yún)?shù)類型,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java14對(duì)于NullPointerException的新處理方式示例解析

    Java14對(duì)于NullPointerException的新處理方式示例解析

    這篇文章主要為大家介紹了Java14對(duì)于NullPointerException的新處理方式示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java Floyd算法求有權(quán)圖(非負(fù)權(quán))的最短路徑并打印

    Java Floyd算法求有權(quán)圖(非負(fù)權(quán))的最短路徑并打印

    這篇文章主要介紹了Java Floyd算法求有權(quán)圖(非負(fù)權(quán))的最短路徑并打印,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-07-07
  • 解決mybatis一對(duì)多查詢r(jià)esultMap只返回了一條記錄問題

    解決mybatis一對(duì)多查詢r(jià)esultMap只返回了一條記錄問題

    小編接到領(lǐng)導(dǎo)一個(gè)任務(wù)需求,需要用到使用resultMap相關(guān)知識(shí),在這小編記錄下這個(gè)問題的解決方法,對(duì)mybatis一對(duì)多查詢r(jià)esultMap項(xiàng)目知識(shí)感興趣的朋友一起看看吧
    2021-11-11
  • Lambda表達(dá)式和Java集合框架

    Lambda表達(dá)式和Java集合框架

    本文主要介紹了Lambda表達(dá)式和Java集合框架的相關(guān)知識(shí),具有很好的參考價(jià)值。下面跟著小編一起來看下吧
    2017-03-03
  • Java中各種集合判空方法總結(jié)

    Java中各種集合判空方法總結(jié)

    最近接觸集合比較多,經(jīng)常對(duì)于集合是否為空做判斷,下面這篇文章主要給大家介紹了關(guān)于Java中各種集合判空方法總結(jié)的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 一個(gè)Java中BigDecimal的問題記錄

    一個(gè)Java中BigDecimal的問題記錄

    這篇文章主要給大家介紹了關(guān)于Java中一個(gè)BigDecimal問題的相關(guān)資料,通過文中介紹的方法可以很方便的解決BigDecimal進(jìn)行計(jì)算的時(shí)候不管怎么計(jì)算,最后得到的值都沒有變化的問題,需要的朋友可以參考下
    2021-11-11
  • Java中為什么不同的返回類型不算方法重載

    Java中為什么不同的返回類型不算方法重載

    這篇文章主要介紹了Java中為什么不同的返回類型不算方法重載,方法重載是指在同一個(gè)類中,定義了多個(gè)同名方法,但每個(gè)方法的參數(shù)類型或者是參數(shù)個(gè)數(shù)不同就是方法重載,下文詳細(xì)介紹,需要的小伙伴可以參考一下
    2022-05-05

最新評(píng)論