jvm垃圾回收(一)

點擊圖片領取阿里云云產品幸運券

jvm中堆和棧的區別

這裏所說的棧不是java.util.Stack的數據結構,而是JVM內存中的區域。堆和棧的區別可以分爲以下幾個方面:

  • 棧內存是用來存放局部變量和方法調用的,而堆內存是用於存儲對象的,比如:Object object = new Object();這條命令會聲明一個object的引用,指向新建的Object對象,其中object變量存儲在棧,而新建的對象則存儲在堆上;
  • 每個線程都有自己獨立的棧空間,其他線程無法進行訪問,也即棧是線程的私有存儲空間;而堆內存對所有的線程可見,堆內存中的對象可以被所有線程訪問;
  • 發生內存用盡的異常不同,棧空間溢出的話拋出的異常爲java.lang.StackOverFlowError;如果堆內存溢出的話,拋出的異常爲java.lang.OutOfMemoryError;
  • 棧的內存空間大小要遠遠小於堆內存空間,這一點顯而易見,因爲棧只用於存儲變量,而堆用於存儲對象,對象往往需要佔用很大的空間。

堆內存是運行時數據區,在運行時,Java實例對象會被創建保存到堆上,當對象不被引用時,這個對象對於Java垃圾回收器來說,就變成可以回收的。
由於對象的生命週期差異很大,爲了更好的實現垃圾回收的效率,堆內存分爲三個區域:年輕代、年老代、永久代。

年輕代又被劃分爲3個區:eden,s0和s1,對象第一次被創建的時候,會存儲到eden區,當eden區的可用空間不足以存放新對象的時候,這時候會觸發垃圾回收的操作,MinorGC將eden區的存活對象複製到s0區,複製完畢之後,將eden區的所有對象全部清空,這時候eden區即可以爲新創建的對象分配存儲空間;當eden區再次空間不足時,會觸發新的垃圾回收操作,這時候會把eden區和s0區的存活對象複製到s1區,如果對象的存活時間達到一定的程度,則會被複制到年老代,複製完畢之後,eden區和s0都被清空。

年老代存放生命週期較長,同時存活機率高的對象,當年輕代的存活時間達到一定程度之後,年輕代的對象會被移動到年老代。年老代的存儲空間不足時,會觸發full GC垃圾回收,也就是MajorGC,MajorGC的垃圾回收算法與年輕代的有所不同。

永久代存放一些應用程序啓動時候加載的類,這些對象的生命週期可能對應於整個程序的運行期間,所以生命週期較年老代要更長。

垃圾回收算法

  • 引用計數法:給對象添加一個引用計數器,每當有新的引用指向該對象時,計數器值就加1;當引用失效時,計數器值就減1;當對象的引用計數爲0時,就可以對其進行垃圾回收;

  • 追蹤引用算法:將所有的對象以及之間的引用關係看作一棵樹,選定一個對象爲根節點,對所有節點進行遍歷,當一個對象到根節點沒有引用鏈相連時,則該對象不可達,該對象是不可使用的,垃圾收集器將回收其所佔的內存。

MinorGC和MajorGC

HotSpot虛擬機的垃圾回收主要分爲兩種MinorGC和MajorGC,其中前者作用於年輕代,後者作用於年老大以及永久代,並且二者所用的垃圾回收算法也有所區別。MinorGC發生在年輕代,由於年輕代的對象生命週期較短,所以MinorGC的發生頻率較高,當然執行速度也快,對於應用程序影響不大;MajorGC發生在年老代和永久代,因爲這兩個區的對象生命週期較長,需要進行垃圾回收的操作頻率較低,另一方面,因爲對象較大,進行垃圾回收的操作會比較耗時,所以應該儘量減少MajorGC的操作,以免對應用程序的運行造成不良影響。

MinorGC

MinorGC作用於年輕代,採用的是複製-刪除算法。當eden區內存空間不足時,引發垃圾回收的操作,存活的對象從eden區複製到survivor區,複製完之後,將eden區所有對象清除。

MajorGC

MajorGC作用於年老代和永久代,當進行MinorGC的時候,年輕代中存活時間達到一定程度的對象會被提升到年老代。MajorGC採用的垃圾回收算法是標記-整理算法,進行垃圾回收的時候,將不再被引用的對象進行標記清除,所有的可回收對象被清除之後,將所有剩下的存活對象移動到一起,消除因爲垃圾回收造成的內存碎片問題。

對年老代和永久代的垃圾回收耗時長、佔用資源多,而且由於垃圾回收是一個單獨的線程運行,所以長時間的垃圾回收操作會給人一種應用程序掛起的狀態,所以應該儘量避免MajorGC的操作,儘量進行MinorGC的操作。在寫代碼的時候,可以通過儘量使用局部變量、對實例變量使用完賦值null以及對list進行clear的操作,來避免產生引用指向不再使用的對象的情況,這樣可以減少MajorGC的操作。

四種不同類型的引用

Java中存在四種類型的引用:強引用、弱引用、軟引用以及幽靈引用(也叫虛引用)。

通常我們說,如果一個對象被一個或者多個引用所指向的話,這個對象是不會被JVM回收的。但是,這要根據引用的類型來決定。

  • 強引用

強引用是最普遍的引用,Object o=new Object(); o就是一個強引用。如果一個對象被強引用所指向,那麼垃圾回收器不會對其進行回收,即便拋出內存溢出的錯誤。

如果強引用的對象需要被垃圾回收器回收,我們可以顯示的通過將引用賦值爲null,來使得對象可以被回收,但是何時回收要取決於JVM

  • 軟引用

如果一個對象沒有強引用指向它,只具有軟引用的話,那麼該對象是否被垃圾回收器回收取決於堆內存空間的可用空間容量。如果堆內存空間足夠,則垃圾回收器不會對其進行回收;如果內存空間比較緊張,那麼垃圾回收器會回收這些對象的內存。

  • 弱引用

弱引用類似於軟引用,區別在於:弱引用指向的對象具有更短暫的生命週期。如果一個對象只有弱引用的話,不管堆內存空間的可用空間如何,垃圾回收器都會對其進行回收。

  • 幽靈引用

幽靈引用不會決定對象的生命週期,如果一個對象僅持有虛引用,那麼就和沒有引用一樣,這個對象隨時都可以被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的情況。

觸發垃圾回收

垃圾回收的操作是由Java虛擬機自動運行的,開發者卻可以通過調用System.gc()或者Runtime.gc()來請求JVM啓動垃圾回收。儘管可以通過這種方式來促使JVM進行垃圾回收,但是最終是由JVM基於eden區內存空間來決定的。


點擊圖片領取阿里云云產品幸運券

發佈了52 篇原創文章 · 獲贊 105 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章