[轉載] JVM中對象的回收過程

參考鏈接: JVM是否創建Main類(具有main()的類)的對象

當我們的程序開啟運行之后就,就會在我們的java堆中不斷的產生新的對象,而這是需要占用我們的存儲空間的,因為創建一個新的對象需要分配對應的內存空間,顯然我的內存空間是固定有限的,所以我們需要對沒有用的對象進行回收,本文就來記錄下JVM中對象的銷毀過程。?

?

?

?文章目錄

?1.怎么判斷對象是沒用的了引用計數算法可達性分析算法

? 2.對象的引用分類強引用軟引用弱引用虛引用

? 3.finalize方法4.方法區的回收

?

?

1.怎么判斷對象是沒用的了?

引用計數算法?

??我們在很多場景中會聽到java對象判斷存活的方式是計算該對象的引用計數器是否為0,如果為0就說明沒有其他變量引用該對象了,這個對象就可以被垃圾收集器回收了。但事實上JVM并不是采用該算法來判斷對象是否可以回收的,比如objectA.a=objectB及objectB.b=objectA除此之外沒有其他引用了。但是按照引用計數算法是不會回收這兩個對象的。但是這兩個對象也已經不能被其他對象訪問了,所以這就是問題。?

可達性分析算法?

??java中判斷對象是否可以回收是通過可達性分析算法來實現的。如下圖:? ??在上圖中object5,object6及object7這三個對象雖然有相互之間的引用,但是通過GC Roots對象并不能引用到這三個對象,所以這三個對象是滿足回收條件的,而對象1到4通過GC Roots可達,所以這幾個對象任然存活。 ??GC Roots并不是一個對象,而是一組對象,在java中可以作為GC Roots對象的有如下幾種:?

序號類型1虛擬機棧(本地變量表)中引用的對象2方法區中類靜態屬性引用的對象3方法區中常量引用的對象4本地方法棧中JNI(一般說的Native方法)引用的對象

2.對象的引用分類?

??判斷對象是否存活我們是通過GC Roots的引用可達性來判斷的,但是引用關系并不止一種,而是有四種分別是:強引用(Strong Reference),軟引用(Soft Reference),弱引用(Weak Reference)和虛引用(Phantom Reference).引用強度依次減弱。??

強引用?

??強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾收器絕不會回收它。當內存空間不足,Java虛擬機寧愿拋出OutOfMmoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用 對象來解決內存不足的問題。?

軟引用?

??軟引用是用來描述一些還有用但并非必須的對象。對于軟引用關聯著的對象,在系統將要發生內存溢出異常之前,將會把這些對象列進回收范圍進行第二次回收。如果這次回收還沒有足夠的內存,才會拋出內存溢出異常。?

/**

?* 軟引用:緩存場景的使用

?* @author dengp

?*

?*/

public class SoftReferenceTest {

?

? ? /**

? ? ?* 運行參數 -Xmx200m -XX:+PrintGC

? ? ?* @param args

? ? ?* @throws InterruptedException

? ? ?*/

? ? public static void main(String[] args) throws InterruptedException {

? ? ? ? //存儲100M的緩存數據

? ? ? ? byte[] cacheData = new byte[100 * 1024 * 1024];

? ? ? ? //將緩存數據用軟引用持有

? ? ? ? SoftReference<byte[]> cacheRef = new SoftReference<>(cacheData);

? ? ? ? //將緩存數據的強引用去除

? ? ? ? cacheData = null;

? ? ? ? System.out.println("第一次GC前" + cacheData);

? ? ? ? System.out.println("第一次GC前" + cacheRef.get());

? ? ? ? //進行一次GC后查看對象的回收情況

? ? ? ? System.gc();

? ? ? ? //等待GC

? ? ? ? Thread.sleep(500);

? ? ? ? System.out.println("第一次GC后" + cacheData);

? ? ? ? System.out.println("第一次GC后" + cacheRef.get());

?

? ? ? ? //在分配一個120M的對象,看看緩存對象的回收情況

? ? ? ? // 空間不夠

? ? ? ? byte[] newData = new byte[120 * 1024 * 1024];

? ? ? ? System.out.println("分配后" + cacheData);

? ? ? ? System.out.println("分配后" + cacheRef.get());

?

? ? }

}

?

輸出結果?

第一次GC前null

第一次GC前[B@15db9742

[GC (System.gc())? 104396K->103072K(175104K), 0.0054505 secs]

[Full GC (System.gc())? 103072K->102931K(175104K), 0.0095426 secs]

第一次GC后null

第一次GC后[B@15db9742

[GC (Allocation Failure)? 103597K->102995K(175104K), 0.0099572 secs]

[GC (Allocation Failure)? 102995K->102963K(175104K), 0.0044781 secs]

[Full GC (Allocation Failure)? 102963K->102931K(175104K), 0.0226699 secs]

[GC (Allocation Failure)? 102931K->102931K(199680K), 0.0022288 secs]

[Full GC (Allocation Failure)? 102931K->519K(131072K), 0.0226120 secs]

分配后null

分配后null

?

??從上面的示例中就能看出,軟引用關聯的對象不會被GC回收。JVM在分配空間時,如果Heap空間不足,就會進行相應的GC,但是這次GC并不會收集軟引用關聯的對象,但是在JVM發現就算進行了一次回收后還是不足(Allocation Failure),JVM會嘗試第二次GC,回收軟引用關聯的對象。 ??像這種如果內存充足,GC時就保留,內存不夠,GC再來收集的功能很適合用在緩存的引用場景中。在使用緩存時有一個原則,如果緩存中有就從緩存獲取,如果沒有就從數據庫中獲取,緩存的存在是為了加快計算速度,如果因為緩存導致了內存不足進而整個程序崩潰,那就得不償失了。?

弱引用?

??弱引用也是用來描述非必須對象的,他的強度比軟引用更弱一些,被弱引用關聯的對象,在垃圾回收時,如果這個對象只被弱引用關聯(沒有任何強引用關聯他),那么這個對象就會被回收?

/**

?* 弱引用

?* @author dengp

?*

?*/

public class WeakReferenceTest {

?

? ? /**

? ? ?* 運行參數 -Xmx200m -XX:+PrintGC

? ? ?* @param args

? ? ?* @throws InterruptedException

? ? ?*/

? ? public static void main(String[] args) throws InterruptedException {

? ? ? ? //存儲100M的緩存數據

? ? ? ? byte[] cacheData = new byte[100 * 1024 * 1024];

? ? ? ? //將緩存數據用軟引用持有

? ? ? ? WeakReference<byte[]> cacheRef = new WeakReference<>(cacheData);

? ? ? ? //將緩存數據的強引用去除

? ? ? ? cacheData = null;

? ? ? ? System.out.println("第一次GC前" + cacheData);

? ? ? ? System.out.println("第一次GC前" + cacheRef.get());

? ? ? ? //進行一次GC后查看對象的回收情況

? ? ? ? System.gc();

? ? ? ? //等待GC

? ? ? ? Thread.sleep(500);

? ? ? ? System.out.println("第一次GC后" + cacheData);

? ? ? ? System.out.println("第一次GC后" + cacheRef.get());

?

? ? ? ? //在分配一個120M的對象,看看緩存對象的回收情況

? ? ? ? byte[] newData = new byte[120 * 1024 * 1024];

? ? ? ? System.out.println("分配后" + cacheData);

? ? ? ? System.out.println("分配后" + cacheRef.get());

? ? }

}

?

輸出結果?

第一次GC前null

第一次GC前[B@15db9742

[GC (System.gc())? 104396K->103072K(175104K), 0.0013337 secs]

[Full GC (System.gc())? 103072K->531K(175104K), 0.0070222 secs]

第一次GC后null

第一次GC后null

分配后null

分配后null

?

弱引用直接被回收掉了。那么弱引用的作用是什么?或者使用場景是什么呢??

static Map<Object,Object> container = new HashMap<>();

public static void putToContainer(Object key,Object value){

? ? container.put(key,value);

}

?

public static void main(String[] args) {

? ? //某個類中有這樣一段代碼

? ? Object key = new Object();

? ? Object value = new Object();

? ? putToContainer(key,value);

?

? ? //..........

? ? /**

? ? ?* 若干調用層次后程序員發現這個key指向的對象沒有用了,

? ? ?* 為了節省內存打算把這個對象拋棄,然而下面這個方式真的能把對象回收掉嗎?

? ? ?* 由于container對象中包含了這個對象的引用,所以這個對象不能按照程序員的意向進行回收.

? ? ?* 并且由于在程序中的任何部分沒有再出現這個鍵,所以,這個鍵 / 值 對無法從映射中刪除。

? ? ?* 很可能會造成內存泄漏。

? ? ?*/

? ? key = null;

}

?

在《Java核心技術卷1》這本書中對此做了說明?

?

?設計 WeakHashMap類是為了解決一個有趣的問題。如果有一個值,對應的鍵已經不再 使用了, 將會出現什么情況呢? 假定對某個鍵的最后一次引用已經消亡,不再有任何途徑引 用這個值的對象了。但是,由于在程序中的任何部分沒有再出現這個鍵,所以,這個鍵 / 值 對無法從映射中刪除。為什么垃圾回收器不能夠刪除它呢? 難道刪除無用的對象不是垃圾回 收器的工作嗎??

?

?

?遺憾的是,事情沒有這樣簡單。垃圾回收器跟蹤活動的對象。只要映射對象是活動的, 其中的所有桶也是活動的, 它們不能被回收。因此,需要由程序負責從長期存活的映射表中 刪除那些無用的值。 或者使用 WeakHashMap完成這件事情。當對鍵的唯一引用來自散列條目時, 這一數據結構將與垃圾回收器協同工作一起刪除鍵 / 值對。?

?

?

?下面是這種機制的內部運行情況。WeakHashMap 使用弱引用(weak references) 保存鍵。 WeakReference 對象將引用保存到另外一個對象中,在這里,就是散列鍵。對于這種類型的 對象,垃圾回收器用一種特有的方式進行處理。通常,如果垃圾回收器發現某個特定的對象 已經沒有他人引用了,就將其回收。然而, 如果某個對象只能由 WeakReference 引用, 垃圾 回收器仍然回收它,但要將引用這個對象的弱引用放人隊列中。WeakHashMap將周期性地檢 查隊列, 以便找出新添加的弱引用。一個弱引用進人隊列意味著這個鍵不再被他人使用, 并 且已經被收集起來。于是, WeakHashMap將刪除對應的條目。?

?

除了WeakHashMap使用了弱引用,ThreadLocal類中也是用了弱引用,可以自行了解下。?

虛引用?

??一個對象是否有虛引用的存在,完全不會對其生存時間構成影響,也無法通過虛引用來獲取一個對象的實例。為一個對象設置虛引用關聯的唯一目的就是能在這個對象被收集器回收時收到一個系統通知?

3.finalize方法?

??當一個對象在堆內存中運行時,根據它被引用變量所引用的狀態,可以把它所處的狀態? 可達狀態:當一個對象被創建后,若有一個以上的引用變量引用它,則這個對象在程序中處于可達狀態。 可恢復狀態:如果程序中某個對象不再有任何引用變量引用它,它就進入了可恢復狀態。此時,系統的垃圾回收機制準備回收該對象所占用的內存,在回收該對象之前,系統會調用所有可恢復狀態對象的finalize()方法進行資源清理。如果系統在調用finalize()方法時重新讓一個引用變量引用該對象,則這個對象會再次變成可達狀態;否則該對象將進入不可達狀態。 不可達狀態:當對象與所有引用變量的關聯都被切斷,且系統已經調用所有對象的finalize()方法后依然沒有使該對象變成可達狀態,那么這個對象將永久性地失去引用,最后變成不可達狀態。只有當一個對象處于不可達狀態時,系統才會真正回收該對象所占有的資源。?

所以finalize方法是對象存活的最后一次機會,而且只會執行一次。可以將可恢復狀態轉變為可達狀態。?

銷毀一個對象過程歸納如下:??

4.方法區的回收?

??很多人認為方法區(或者HotSpot虛擬機中的永久代)是沒有垃圾收集的,Java虛擬機規范中確實說過可以不要求虛擬機在方法區實現垃圾收集,而且在方法區進行垃圾收集的“性價比”一般比較低:在堆中,尤其是在新生代中,常規應用進行一次垃圾收集一般可以回收70%~95%的空間,而永久代的垃圾收集效率遠低于此。?

??永久代的垃圾收集主要回收兩部分內容:廢棄常量和無用的類。回收廢棄常量與回收Java堆中的對象非常類似。以常量池中字面量的回收為例,假如一個字符串“abc”已經進入了常量池中,但是當前系統沒有任何一個String對象是叫做“abc”的,換句話說是沒有任何String對象引用常量池中的“abc”常量,也沒有其他地方引用了這個字面量,如果在這時候發生內存回收,而且必要的話,這個“abc”常量就會被系統“請”出常量池。常量池中的其他類(接口)、方法、字段的符號引用也與此類似。?

??判定一個常量是否是“廢棄常量”比較簡單,而要判定一個類是否是“無用的類”的條件則相對苛刻許多。類需要同時滿足下面3個條件才能算是“無用的類”:?

該類所有的實例都已經被回收,也就是Java堆中不存在該類的任何實例。加載該類的ClassLoader已經被回收。該類對應的java.lang.Class 對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。?

??虛擬機可以對滿足上述3個條件的無用類進行回收,這里說的僅僅是“可以”,而不是和對象一樣,不使用了就必然會回收。是否對類進行回收,HotSpot虛擬機提供了-Xnoclassgc參數進行控制,還可以使用-verbose:class及-XX:+TraceClassLoading、 -XX:+TraceClassUnLoading查看類的加載和卸載信息。?

??在大量使用反射、動態代理、CGLib等bytecode框架的場景,以及動態生成JSP和OSGi這類頻繁自定義ClassLoader的場景都需要虛擬機具備類卸載的功能,以保證永久代不會溢出。?

參考《深入理解Java虛擬機》

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/540427.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/540427.shtml
英文地址,請注明出處:http://en.pswp.cn/news/540427.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

c語言格式對齊填充_C ++中類的大小 課堂上的填充和對齊| 派生類的大小

c語言格式對齊填充Prerequisite: 先決條件&#xff1a; sizeof() operator in C/C C / C 中的sizeof()運算符 Size of struct in C C中的struct大小 We know that a struct size is not only the summation of all the data members, rather its the minimum sum guaranteed. …

ELK系列~對fluentd參數的理解

這段時候一直在研究ELK框架&#xff0c;主要集成在對fluentd和nxlog的研究上&#xff0c;國內文章不多&#xff0c;主要看了一下官方的API&#xff0c;配合自己的理解&#xff0c;總結了一下&#xff0c;希望可以幫到剛入行的朋友們&#xff01; Fluentd&#xff08;日志收集與…

[轉載] Java中的50個關鍵字

參考鏈接&#xff1a; Java平臺如何獨立 Java中的50個關鍵字 關鍵字也稱為保留字&#xff0c;是指java語言中規定了特定含義的標示符。對于保留字&#xff0c;用戶只能按照系統規定的方式使用&#xff0c;不能自行定義。Java中有50個常用關鍵字&#xff1a; 與數據類型相關…

MySQL 直接存儲圖片并在 html 頁面中展示,點擊下載

數據庫實體類&#xff1a; package com.easy.kotlin.picturecrawler.entityimport java.util.* import javax.persistence.*Entity Table(indexes arrayOf(Index(name "idx_url", unique true, columnList "url"),Index(name "idx_category"…

css 文本背景色透明_如何使用CSS將文本或圖像的背景設置為透明?

css 文本背景色透明Introduction: 介紹&#xff1a; In web development, there are numerous ways by which we can style our websites or web pages. You can make use of lots of properties for creating attractive and responsive websites. 在Web開發中&#xff0c;我…

[轉載] 1.1Java使用JDBC原生方式連接MySql數據庫

參考鏈接&#xff1a; Java數據庫連接JDBC驅動程序 前言&#xff1a;今天有朋友問我原生的java連接數據庫&#xff0c;因為框架的使用&#xff0c;如果基礎不牢固的人&#xff0c;是很容易遺忘原生的連接方式。今天正好趁此做一下回顧&#xff1a; 這里只考慮原生方式&#x…

maven安裝及集成myeclipse

第一步&#xff1a;下載和安裝 1、官網下載Maven&#xff1a;http://maven.apache.org/download.cgi 2、解壓到一個文件夾2、設置環境變量&#xff1a;如&#xff1a;M2_HOME&#xff1a;D:\JAVA\apache-maven-3.0.5在path中添加;%M2_HOME%\bin;第二步&#xff1a;和MyEclipse集…

[轉載] Java泛型詳解:<T>和Class<T>的使用。泛型類,泛型方法的詳細使用實例

參考鏈接&#xff1a; Java中的main()函數是強制性的嗎 一、引入 1、泛型是什么 首先告訴大家ArrayList就是泛型。那ArrayList能完成哪些想不到的功能呢&#xff1f;先看看下面這段代碼&#xff1a; [java] view plain copy ArrayList<String> strList new ArrayL…

數字和數字根的總和_使用8086微處理器查找8位數字的數字總和

數字和數字根的總和Problem statement: 問題陳述&#xff1a; Write an assembly language program in 8086 microprocessor to find sum of digit of an 8 bits number using 8 bits operation. 在8086微處理器中編寫匯編語言程序&#xff0c;以使用8位運算找到8位數字的位數…

[轉載] Java筆試題集錦

參考鏈接&#xff1a; 關于Java中文件名和類名的誤解 Java筆試題集錦 1.MVC的各個部分都有那些技術來實現?如何實現? 答&#xff1a;MVC是Model&#xff0d;View&#xff0d;Controller的簡寫。"Model" 代表的是應用的業務邏輯&#xff08;通過JavaBean&#xff…

gcc -pthread_錯誤-在GCC Linux中使用C程序未定義對'pthread_create'的引用

gcc -pthread在Linux中修復對pthread_create的未定義引用 (Fixing undefined reference to pthread_create in Linux) This is a common error while compiling C program in GCC/G Linux. This error occurs when you are using pthread_create function to create threads in…

[轉載] Java面試題全集(上)

參考鏈接&#xff1a; 如何運行不同目錄中的Java類文件 2013年年底的時候&#xff0c;我看到了網上流傳的一個叫做《Java面試題大全》的東西&#xff0c;認真的閱讀了以后發現里面的很多題目是重復且沒有價值的題目&#xff0c;還有不少的參考答案也是錯誤的&#xff0c;于是我…

python重載運算符乘法_Python | 使用乘法運算符創建一個字符串的多個副本

python重載運算符乘法Given a string and we have to create its multiple copies by using multiplication operator in Python? 給定一個字符串&#xff0c;我們必須通過在Python中使用乘法運算符來創建其多個副本&#xff1f; If you want to create multiple copies of …

一次前端筆試總結

1.有一個長度未知的數組a&#xff0c;如果它的長度為0就把數字1添加到數組里面&#xff0c;否則按照先進先出的隊列規則讓第一個元素出隊。 分析&#xff1a;這道題主要是考核了數組的隊列方法和棧方法。另外&#xff0c;原題還有字數限制的&#xff0c;只有在字數小于30并且結…

Java文件類boolean setLastModified(long set_new_time)方法,包含示例

文件類boolean setLastModified(long set_new_time) (File Class boolean setLastModified(long set_new_time)) This method is available in package java.io.File.setLastModified(long set_new_time). 軟件包java.io.File.setLastModified(long set_new_time)中提供了此方法…

[轉載] Linux里面的文件目錄類指令

參考鏈接&#xff1a; 如何運行不同目錄中的Java類文件 引用&#xff1a;尚硅谷韓老師的《尚硅谷-Linux-經典升級》 日常總結 pwd 指令 &#xff08;顯示當前工作目錄的絕對路徑&#xff09; 基本語法 pwd (功能描述&#xff1a;顯示當前工作目錄的絕對路徑) …

[轉載] 微服務安全和治理

參考鏈接&#xff1a; 微服務介紹 在整體式架構中&#xff0c;由于運行應用程序的運行時環境相對隔離&#xff0c;所以治理和安全保護很簡單。微服務架構具有典型的革新特征&#xff0c;給活動的治理和應用程序的安全威脅保護帶來了更多挑戰。 微服務架構中的安全性 微服務…

SSL

今天遇到一位網友要求老蔣將他當前已經在使用的WDCP面板環境&#xff0c;給某個站點添加SSL證書&#xff0c;實現HTTPS網址訪問。在過去的幾篇文章中&#xff0c;老蔣也有分享過不少在Linux VPS中對應的WEB環境安裝SSL證書的經歷&#xff0c;其實總體來看都大同小異&#xff0c…

[轉載] Java中如何引用另一個類里的集合_Java工程師面試題整理

參考鏈接&#xff1a; 在Java中將預定義的類名用作類或變量名 花了一星期把學過的都整理一遍 盡量易懂&#xff0c;從基礎到框架 最新版大廠面經匯總出爐&#xff0c;持續更新中 匯總完了上傳網盤&#xff0c;設計到后端架構師的一切知識 如果沒更新就代表我死了 一&#xff0…

應用寶認領應用

2019獨角獸企業重金招聘Python工程師標準>>> 【Android】命令行jarsigner簽字和解決找不到證書鏈錯誤 1、簽名失敗 $jarsigner -verbose -keystore /Volumes/Study/resourcesLib/Qunero-achivements/AndroidApp/QuLordy-signed-key -signedjar ./signed_XiaomiVerif…