這篇文章主要介紹了淺談Java回收對象的標記和對象的二次標記過程的相關內容,小編覺得還是挺不錯的,這裡給大家分享一下,需要的朋友可以參考。
一、物件的標記
1、什麼是標記?怎麼標記?
第一題相信大家都知道,標記就是對一些已死的對像打上記號,方便垃圾收集器的清理。 至於怎麼標記,一般有兩種方法:引用計數和可達性分析。
引用計數實現起來比較簡單,就是給物件添加一個引用計數器,每當有一個地方引用它時就加1,引用失效時就減1,當計數器為0的時候就標記為可回收。這種判斷效率很高,但是很多主流的虛擬機器並沒有採用這種方法,主要是因為它很難解決幾個物件之間循環引用的問題,雖然不怎麼用了,但還是值得我們學習!
public class Test { private Object obj; Public static void main(){ Test t1=new Test(); Test t2=new Test(); t1.obj=t2; t2.obj=t1; t1=null; t2=null; //如果对象在这行发生gc,那么t1和t2对象是否能被回收 System.gc(); } }
可達性分析的基本想法是:透過將一些稱為"GC Roots"的物件作為起始點,從這些節點開始搜索,搜索和該節點發生直接或間接引用關係的對象,將這些對像以鏈的形式組合起來,形成一張“關係網”,又叫做引用鏈。最後垃圾收集器就回收一些不在這張關係網上的物件。如圖:
連接GC Roots對象的object是確定還存活的對象,而右邊的die obj由於和GCROOTS沒有關係,所以會標記為可回收的對象。目前主流的商用虛擬機器用的都是類似的方法。那什麼對象才能當「GC Roots」呢?在java中,有四種物件可以作為「GC Roots」
1:堆疊幀(第一章的名詞)中的引用對象。 (堆疊中的)
2:靜態屬性所引用的物件。 (方法區中的)
3:常數引用的物件。 (方法區中的)
4:本地方法堆疊中JNI所引用的物件。 (本地方法堆疊中的)
二、物件的二次回收
說過物件的標記,但是不是被標記了就一定會被回收呢?不知道小夥伴們記不記得Object類別有一個finalize()方法,所有類別都繼承了Object類,因此也預設實作了這個方法。
finalize的工作原理應該是這樣的:一旦垃圾收集器準備好釋放物件所佔用的儲存空間,它首先呼叫finalize(),而且只有在下一次垃圾收集過程中,才會真正回收物件的記憶體.所以如果使用finalize(),就可以在垃圾收集期間進行一些重要的清除或清掃工作.
finalize()在什麼時候被呼叫?
有三種情況
1.所有物件被Garbage Collection時自動呼叫,例如執行System.gc()的時候.
2.程式退出時為每個物件呼叫一次finalize方法。
3.明確的呼叫finalize方法
這個方法的用途是:在該物件被回收之前,該物件的finalize()方法會被調用。 這裡的回收之前指的就是被標記之後,問題就出在這裡,有沒有一種情況就是原本一個物件開始不再上一章所講的「關係網」(引用鏈)中,但是當開發者重寫了finalize()後,並且將該物件重新加入到了「關係網」中,也就是說該物件對我們還有用,不應該被回收,但是已經被標記啦,怎麼辦呢?
針對這個問題,虛擬機器的做法是進行兩次標記,也就是第一次標記不在「關係網」中的物件。第二次的話就要先判斷該物件有沒有實作finalize()方法了,如果沒有實作就直接判斷該物件可回收;如果實作了就會先放在一個佇列中,並由虛擬機建立的一個低優先順序的執行緒去執行它,隨後就會進行第二次的小規模標記,在這次被標記的物件就會真正的被回收了。
總結:簡單說,物件先進行第一次標記,在下一次GC之前會執行物件的finalize()方法。在執行finalize()方法的時候判斷物件是否實作了finalize()方法,沒有實作直接清除;實作了,將物件放在一個佇列中執行finalize方法,進行第二次標記
在java根搜尋演算法中判斷對象的可及性,對於不可達的對象,也不一定是必須清理。這時候有一個緩刑期,真正的判斷一個對象死亡,至少要經過倆次標記過程:如果對像在進行根搜索後發現沒有與GC roots相關聯的引用鏈,那他將會第一次標記並且進行一次篩選,篩選的條件是此物件是否有必要執行finalize()方法,當物件沒有覆寫finalize()方法,或finalize()方法已經被虛擬機器呼叫過,虛擬機器將這倆種情況都視為「沒有必要執行」。
即當一個物件重寫了finalize()方法的時候,這個物件被判定為有必要執行finalize()方法,那麼這個物件被放置在F-Queue隊列之中,並在稍後由一條由虛擬機器自動建立的、低優先順序的Finalizer執行緒去執行。這裡所謂的執行是指虛擬機會出發這個方法,但不承諾會等待它運行結束。這樣做的原因:如果一個物件在finalize()方法中執行緩慢,或者發生了死循環(極端的情況下),將可能會導致F-Queue隊列中的其他物件永久處於等待狀態,甚至導致整個內存回收系統崩潰。 finalize()方法是物件逃脫死亡命運的最後一次機會,稍後GC將對F-Queue中的物件進行第二次小規模的標記,如果物件要在finalize()中成功拯救自己----只要重新與引用鏈上的任何建立關聯即可,那麼在第二次標記時它將會被移出「即將回收」的集合;如果物件此時沒有逃脫,就會被回收。程式碼範例:參考《深入理解java虛擬機器》對應章節
總結
#以上是Java中回收物件的標記以及物件的二次標記過程詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

類加載器通過統一的類文件格式、動態加載、雙親委派模型和平台無關的字節碼,確保Java程序在不同平台上的一致性和兼容性,實現平台獨立性。

Java編譯器生成的代碼是平台無關的,但最終執行的代碼是平台特定的。 1.Java源代碼編譯成平台無關的字節碼。 2.JVM將字節碼轉換為特定平台的機器碼,確保跨平台運行但性能可能不同。

多線程在現代編程中重要,因為它能提高程序的響應性和資源利用率,並處理複雜的並發任務。 JVM通過線程映射、調度機制和同步鎖機制,在不同操作系統上確保多線程的一致性和高效性。

Java的平台獨立性是指編寫的代碼可以在任何安裝了JVM的平台上運行,無需修改。 1)Java源代碼編譯成字節碼,2)字節碼由JVM解釋執行,3)JVM提供內存管理和垃圾回收功能,確保程序在不同操作系統上運行。

Javaapplicationscanindeedencounterplatform-specificissuesdespitetheJVM'sabstraction.Reasonsinclude:1)Nativecodeandlibraries,2)Operatingsystemdifferences,3)JVMimplementationvariations,and4)Hardwaredependencies.Tomitigatethese,developersshould:1)Conduc

云计算显著提升了Java的平台独立性。1)Java代码编译为字节码,由JVM在不同操作系统上执行,确保跨平台运行。2)使用Docker和Kubernetes部署Java应用,提高可移植性和可扩展性。

Java'splatformindependenceallowsdeveloperstowritecodeonceandrunitonanydeviceorOSwithaJVM.Thisisachievedthroughcompilingtobytecode,whichtheJVMinterpretsorcompilesatruntime.ThisfeaturehassignificantlyboostedJava'sadoptionduetocross-platformdeployment,s

容器化技術如Docker增強而非替代Java的平台獨立性。 1)確保跨環境的一致性,2)管理依賴性,包括特定JVM版本,3)簡化部署過程,使Java應用更具適應性和易管理性。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser
Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Atom編輯器mac版下載
最受歡迎的的開源編輯器

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。