成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JVM源碼分析之FinalReference完全解讀

開發 開發工具
JAVA對象引用體系除了強引用之外,出于對性能、可擴展性等方面考慮還特地實現了四種其他引用:SoftReference、WeakReference、PhantomReference、FinalReference,本文主要想講的是FinalReference,因為我們在使用內存分析工具比如zprofiler、mat等在分析一些oom的heap的時候,經常能看到 java.lang.ref.Finalizer占用的內存大小遠遠排在前面,而這個類占用的內存大小又和我們這次的主角FinalReference有著密不可分的關系。

[[181424]]

概述

JAVA對象引用體系除了強引用之外,出于對性能、可擴展性等方面考慮還特地實現了四種其他引用:SoftReference、WeakReference、PhantomReference、FinalReference,本文主要想講的是FinalReference,因為我們在使用內存分析工具比如zprofiler、mat等在分析一些oom的heap的時候,經常能看到 java.lang.ref.Finalizer占用的內存大小遠遠排在前面,而這個類占用的內存大小又和我們這次的主角FinalReference有著密不可分的關系。

對于FinalReference及關聯的內容,我們可能有如下印象:

  • 自己代碼里從沒有使用過
  • 線程dump之后,我們能看到一個叫做Finalizer的java線程
  • 偶爾能注意到java.lang.ref.Finalizer的存在
  • 我們在類里可能會寫finalize方法

那FinalReference到底存在的意義是什么,以怎樣的形式和我們的代碼相關聯呢,這是本文要理清的問題。

JDK中的FinalReference

首先我們看看FinalReference在JDK里的實現:

大家應該注意到了類訪問權限是package的,這也就意味著我們不能直接去對其進行擴展,但是JDK里對此類進行了擴展實現java.lang.ref.Finalizer,這個類也是我們在概述里提到的,而此類的訪問權限也是package的,并且是final的,意味著真的不能被擴展了,接下來的重點我們圍繞java.lang.ref.Finalizer展開(PS:后續講Finalizer相關的其實也就是在說FinalReference)

Finalizer的構造函數

從構造函數上我們獲得下面的幾個關鍵信息 * private:意味著我們在外面無法自己構建這類對象 * finalizee參數:FinalReference指向的對象引用 * 調用add方法:將當前對象插入到Finalizer對象鏈里,鏈里的對象和Finalizer類靜態相關聯,言外之意是在這個鏈里的對象都無法被gc掉,除非將這種引用關系剝離掉(因為Finalizer類無法被unload)。

雖然外面無法創建Finalizer對象,但是注意到有一個register的靜態方法,在方法里會創建這種對象,同時將這個對象加入到Finalizer對象鏈里,這個方法是被vm調用的,那么問題來了,vm在什么情況下會調用這個方法呢?

Finalizer對象何時被注冊到Finalizer對象鏈里

類其實有挺多的修飾,比如final,abstract,public等等,如果一個類有final修飾,我們就說這個類是一個final類,上面列的都是語法層面我們可以顯示標記的,在jvm里其實還給類標記其他一些符號,比如finalizer,表示這個類是一個finalizer類(為了和java.lang.ref.Fianlizer類進行區分,下文要提到的finalizer類的地方都說成f類),gc在處理這種類的對象的時候要做一些特殊的處理,如在這個對象被回收之前會調用一下它的finalize方法。

如何判斷一個類是不是一個f類

在講這個問題之前,我們先來看下java.lang.Object里的一個方法

在Object類里定義了一個名為finalize的空方法,這意味著Java世界里的所有類都會繼承這個方法,甚至可以覆寫該方法,并且根據方法覆寫原則,如果子類覆蓋此方法,方法訪問權限都是至少是protected級別的,這樣其子類就算沒有覆寫此方法也會繼承此方法。

而判斷當前類是否是一個f類的標準并不僅僅是當前類是否含有一個參數為空,返回值為void的名為finalize的方法,而另外一個要求是finalize方法必須非空,因此我們的Object類雖然含有一個finalize方法,但是并不是一個f類,Object的對象在被gc回收的時候其實并不會去調用它的finalize方法。

需要注意的是我們的類在被加載過程中其實就已經被標記為是否為f類了(遍歷所有方法,包括父類的方法,只要有一個非空的參數為空返回void的finalize方法就認為是一個f類)。

f類的對象何時傳到Finalizer.register方法

對象的創建其實是被拆分成多個步驟的,比如A a=new A(2)這樣一條語句對應的字節碼如下:

先執行new分配好對象空間,然后再執行invokespecial調用構造函數,jvm里其實可以讓用戶選擇在這兩個時機中的任意一個將當前對象傳遞給Finalizer.register方法來注冊到Finalizer對象鏈里,這個選擇依賴于RegisterFinalizersAtInit這個vm參數是否被設置,默認值為true,也就是在調用構造函數返回之前調用Finalizer.register方法,如果通過-XX:-RegisterFinalizersAtInit關閉了該參數,那將在對象空間分配好之后就將這個對象注冊進去。

另外需要提一點的是當我們通過clone的方式復制一個對象的時候,如果當前類是一個f類,那么在clone完成的時候將調用Finalizer.register方法進行注冊。

hotspot如何實現f類對象在構造函數執行完畢后調用Finalizer.register

這個實現比較有意思,在這里簡單提一下,我們知道一個構造函數執行的時候,會去調用父類的構造函數,主要是為了能對繼承自父類的屬性也能做初始化,那么任何一個對象的初始化最終都會調用到Object的空構造函數里(任何空的構造函數其實并不空,會含有三條字節碼指令,如下代碼所示),為了不對所有的類的構造函數都做埋點調用Finalizer.register方法,hotspot的實現是在Object這個類在做初始化的時候將構造函數里的return指令替換為_return_register_finalizer指令,該指令并不是標準的字節碼指令,是hotspot擴展的指令,這樣在處理該指令的時候調用Finalizer.register方法,這樣就在侵入性很小的情況下***地解決了這個問題。

f類對象的GC回收

FinalizerThread線程

在Finalizer類的clinit方法(靜態塊)里我們看到它會創建了一個FinalizerThread的守護線程,這個線程的優先級并不是***的,意味著在cpu很緊張的情況下其被調度的優先級可能會受到影響

這個線程主要就是從queue里取Finalizer對象,然后執行該對象的runFinalizer方法,這個方法主要是將Finalizer對象從Finalizer對象鏈里剝離出來,這樣意味著下次gc發生的時候就可能將其關聯的f對象gc掉了,***將這個Finalizer對象關聯的f對象傳給了一個native方法invokeFinalizeMethod

其實invokeFinalizeMethod方法就是調了這個f對象的finalize方法,看到這里大家應該恍然大悟了,整個過程都串起來了

f對象的finalize方法拋出異常會導致FinalizeThread退出嗎

不知道大家有沒有想過如果f對象的finalize方法拋了一個沒捕獲的異常,這個FinalizerThread會不會退出呢,細心的讀者看上面的代碼其實就可以找到答案,在runFinalizer方法里對Throwable的異常都進行了捕獲,因此不可能出現FinalizerThread因異常未捕獲而退出的情況。

f對象的finalize方法會執行多次嗎

如果我們在f對象的finalize方法里重新將當前對象賦值出去,變成可達對象,當這個f對象再次變成不可達的時候還會被執行finalize方法嗎?答案是否定的,因為在執行完***次finalize方法之后,這個f對象已經和之前的Finalizer對象關系剝離了,也就是下次gc的時候不會再發現Finalizer對象指向該f對象了,自然也就不會調用這個f對象的finalize方法了。

Finalizer對象何時被放到ReferenceQueue里

除了這里要說的環節之外,整個過程大家應該都比較清楚了。

當gc發生的時候,gc算法會判斷f類對象是不是只被Finalizer類引用(f類對象被Finalizer對象引用,然后放到Finalizer對象鏈里),如果這個類僅僅被Finalizer對象引用的時候,說明這個對象在不久的將來會被回收了現在可以執行它的finalize方法了,于是會將這個Finalizer對象放到Finalizer類的ReferenceQueue里,但是這個f類對象其實并沒有被回收,因為Finalizer這個類還對他們持有引用,在gc完成之前,jvm會調用ReferenceQueue里的lock對象的notify方法(當ReferenceQueue為空的時候,FinalizerThread線程會調用ReferenceQueue的lock對象的wait方法直到被jvm喚醒),此時就會執行上面FinalizeThread線程里看到的其他邏輯了。

Finalizer導致的內存泄露

這里舉一個簡單的例子,我們使用挺廣的socket通信,SocksSocketImpl的父類其實就實現了finalize方法:

其實這么做的主要目的是萬一用戶忘記關閉socket了,那么在這個對象被回收的時候能主動關閉socket來釋放一些系統資源,但是如果真的是用戶忘記關閉了,那這些socket對象可能因為FinalizeThread遲遲沒有執行到這些socket對象的finalize方法,而導致內存泄露,這種問題我們碰到過多次,需要特別注意的是對于已經沒有地方引用的這些f對象,并不會在最近的那一次gc里馬上回收掉,而是會延遲到下一個或者下幾個gc時才被回收,因為執行finalize方法的動作無法在gc過程中執行,萬一finalize方法執行很長呢,所以只能在這個gc周期里將這個垃圾對象重新標活,直到執行完finalize方法從queue里刪除,這樣下次gc的時候就真的是漂浮垃圾了會被回收,因此給大家的一個建議是千萬不要在運行期不斷創建f對象,不然會很悲劇。

Finalizer的客觀評價

上面的過程基本對Finalizer的實現細節進行完整剖析了,java里我們看到有構造函數,但是并沒有看到析構函數一說,Finalizer其實是實現了析構函數的概念,我們在對象被回收前可以執行一些『收拾性』的邏輯,應該說是一個特殊場景的補充,但是這種概念的實現給我們的f對象生命周期以及gc等帶來了一些影響:

  • f對象因為Finalizer的引用而變成了一個臨時的強引用,即使沒有其他的強引用了,還是無法立即被回收
  • f對象至少經歷兩次GC才能被回收,因為只有在FinalizerThread執行完了f對象的finalize方法的情況下才有可能被下次gc回收,而有可能期間已經經歷過多次gc了,但是一直還沒執行f對象的finalize方法
  • cpu資源比較稀缺的情況下FinalizerThread線程有可能因為優先級比較低而延遲執行f對象的finalize方法
  • 因為f對象的finalize方法遲遲沒有執行,有可能會導致大部分f對象進入到old分代,此時容易引發old分代的gc,甚至fullgc,gc暫停時間明顯變長
  • f對象的finalize方法被調用了,但是這個對象其實還并沒有被回收,雖然可能在不久的將來會被回收

【本文是51CTO專欄作者李嘉鵬的原創文章,轉載請通過微信公眾號(你假笨,id:lovestblog)聯系作者本人獲取授權】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 你假笨
相關推薦

2017-01-11 14:02:32

JVM源碼內存

2020-05-26 18:50:46

JVMAttachJava

2017-01-11 14:19:26

JVM源碼All

2017-02-27 11:48:58

JVM源碼分析Java

2020-07-21 14:19:18

JVM編程語言

2024-10-31 09:24:42

2021-03-11 08:10:48

JVM對象的創建School

2011-05-26 10:05:48

MongoDB

2010-09-27 10:30:42

JVM對象生命周期

2021-03-23 09:17:58

SpringMVCHttpServletJavaEE

2021-07-06 09:29:38

Cobar源碼AST

2024-06-13 07:55:19

2012-09-20 10:07:29

Nginx源碼分析Web服務器

2023-02-26 08:42:10

源碼demouseEffect

2011-05-26 16:18:51

Mongodb

2021-07-14 09:18:19

Python插值算法

2022-09-30 15:35:47

json 文件前端開源

2020-10-30 08:35:23

Java Virtua

2021-09-01 07:21:40

ArrayPool源碼Bucket

2023-09-08 08:42:01

數據場景項目
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品在线看 | 91国产精品在线 | 精品久久香蕉国产线看观看亚洲 | 黄色网一级片 | 国产精品一区三区 | 日韩a在线 | 伊人最新网址 | 9191成人精品久久 | 亚洲 成人 在线 | 精品国产乱码久久久久久蜜柚 | 久久精品亚洲 | 欧美日批| 99久久婷婷国产综合精品电影 | 国产精品久久久久久久久久久免费看 | 国产综合久久 | 91高清视频在线观看 | 亚洲三级av | 一区二区三区亚洲 | 一级毛片观看 | 国产精品国产成人国产三级 | 免费在线观看成年人视频 | 91aiai | 欧美一区二区大片 | 久久看片 | 久久免费视频网 | 亚洲一区二区精品视频 | 亚洲欧美中文日韩在线v日本 | 久久不卡日韩美女 | 老司机午夜性大片 | 草在线| 中日韩毛片| 永久av| 国产成人99久久亚洲综合精品 | 91视视频在线观看入口直接观看 | 精品一二三区 | 99re在线播放| 国产成人自拍av | 国产传媒毛片精品视频第一次 | 久久精彩视频 | 亚洲精品亚洲人成人网 | 91玖玖 |