1. 程式人生 > >記憶體回收機制初解

記憶體回收機制初解

【初步學習記憶體回收機制】

jvm記憶體的記憶體回收機制主要是針對堆來講的
因為棧的記憶體回收機制隨著一個基本型別的建立與銷燬而進行建立而銷燬,比較固定。

【如何判斷物件是否死亡】

早期有兩種演算法:

第一種是【引用計數演算法】

    給物件新增一個計數器,物件被引用,則+1,物件引用失效時,就-1。

    任何時刻計數器為0的物件都是可以被清除的。

    但是這裡有一個問題,是我在後面【小狗】那一篇博文中記錄的一個片段,當兩個物件互相引用的時候,  

    他們就算都沒有實際存在價值了,也無法被這種演算法識別。

第二種是【可達性分析演算法】

    首先確定一個【GcRoots】物件作為起始點,向下搜尋,走過的路經叫做【引用鏈】,如果一個物件到沒有與引用鏈相連,則      認為他是沒有存在價值的一個物件,可以進行回收。

    然而被認為沒有存在價值的物件也不是被直接就回收掉的,他會先被標記並進行一次篩選,

    篩選條件:

    此物件是否有必要執行finalize()方法,當物件沒有覆蓋這個方法或者已經被呼叫過,被視為沒必要執行;

    此物件如果有必要執行finalize()方法,這個物件會被放在一個F-Queue的佇列中,並在之後被一個虛擬機器自動建立的,低優先順序的Finalizer執行緒去執行它,所謂的執行時指虛擬機器會觸發這個方法,但並不承諾會等待他結束,這是為了防止發生異常造成記憶體崩潰。之後GC會對F-queue中物件進行第二次標記,如果物件此時與GcRoot的引用鏈連線上了,就逃脫了被回收的命運,反之被回收。

【一種特例物件】

在回收機制演算法的背後,體現的是一個物件的狀態,分為被引用和沒有被引用兩種。

但實際對於記憶體分析來說,還有一種物件,沒什麼實際用處,但是依然在被引用著,

是否可以有一種方式,可以讓這些雞肋的物件在記憶體充足時可以保留,記憶體不足時則被拋除。

因此jvm1.2之後,java對引用的概念進行了擴充,將引用分為了【強/軟/弱/虛】四種類型:

【強引用】:new一個物件這種普遍存在的;

【軟引用】:softReference類,描述的時有些用但非必需的物件;系統要發生oom異常之前,將會把物件收進回收範圍內進行二次回收,若果仍然不能提供足夠記憶體,才會報異常。

【弱引用】:強度比軟引用更弱一些,弱引用的物件只能存活到下一次GC之前,由weakReference實現。

【虛引用】:需引用不會對物件的生存時間構成影響,也無法通過虛引用來建立物件例項,作用是這個物件被回收時,會獲得一個系統通知,由phantomReference實現。

【垃圾收集演算法】: 

【複製演算法】

這種演算法會將記憶體劃分為兩個相等的塊,每次只使用其中一塊。當這塊記憶體不夠使用時,就將還存活的物件複製到另一塊記憶體中,然後把這塊記憶體一次清理掉。這樣做的效率比較高,也避免了記憶體碎片。但是這樣記憶體的可使用空間減半,是個不小的損失。

把堆均分成兩個大小相同的區域,只使用其中的一個區域,直到該區域消耗完。此時垃圾回收器終端程式的執行,通過遍歷把所有活動的物件複製到另一個區域,複製過程中它們是緊挨著佈置的,這樣也可以達到消除記憶體碎片的目的。複製結束後程序會繼續執行,直到該區域被用完。 
但是,這種方法有兩個缺陷:
對於指定大小的堆,需要兩倍大小的記憶體空間,
需要中斷正在執行的程式,降低了執行效率

【標記-清除演算法】
這種垃圾回收一次回收分為兩個階段:標記、清除。首先標記所有需要回收的物件,在標記完成後回收所有被標記的物件。這種回收演算法會產生大量不連續的記憶體碎片,當要頻繁分配一個大物件時,jvm在新生代中找不到足夠大的連續的記憶體塊,會導致jvm頻繁進行記憶體回收(目前有機制,對大物件,直接分配到老年代中)
【標記-整理演算法】

這是標記-清除演算法的升級版。在完成標記階段後,不是直接對可回收物件進行清理,而是讓存活物件向著一端移動,然後清理掉邊界以外的記憶體

【分代收集演算法】

當前商業虛擬機器都採用這種演算法。
首先根據物件存活週期的不同將記憶體分為幾塊即新生代、老年代,
然後根據不同年代的特點,採用不同的收集演算法。
在新生代中,每次垃圾收集時都有大量物件死去,只有少量存活,所以選擇了複製演算法。

而老年代中因為物件存活率比較高,所以採用標記-整理演算法(或者標記-清除演算法)

新生代:包括一個Eden以及兩個Survivor區,分別稱為s0,s1;初始化後,第一次堆中的物件,放置在Eden中,當此區域記憶體滿了之後,進行一次GC,然後將Eden中存活的物件放到s0中,每次存貨物件被GC移動至其他區的時候,內建計數器+1,【預設當計數器到了15次後,將存貨物件放入老年區,此閾值可配置】當Eden第二次記憶體滿了之後,GC檢視Eden及s0區,將存活物件放入s1,並清空Eden及s0,若s1記憶體滿了,存貨物件也會進入老年區;當Eden第三次滿了之後,GC會檢視Eden及s1區,然後存活物件放入s0,以此類推。s0,s1交替存放存活的物件。

老年代:如果某個物件經歷了幾次垃圾回收之後還存活【上面提了預設8次】,就會被存放到老年代中。老年代的空間一般比新生代大。

【垃圾收集器】:

   這裡只作簡單介紹:

Serial收集器:最基本的收集齊,單執行緒,在gc時,需要暫停所有其他執行緒的工作。emmmm

ParNew收集器:Serial的多執行緒版本,使用複製演算法。

Parallel Scavenge收集器:與ParNew相似,但是關注點不同,其他收集器關注點是縮短GC時,減少使用者執行緒的停頓時間,而這個收集齊的目標是i達到一個可控制的吞吐量。

SerialOld收集器,使用標記整理,單執行緒。

ParallelOld收集器,使用標記整理,多執行緒。

CMS收集器:獲取最短回收停頓時間為目標的收集器。

G1收集器:最新的了,是面試的重點。

(CMS和G1我會在後續深入學習後,再寫一篇相關的部落格)

【幾種常用的GC】

Minor GC

從年輕代空間(包括 Eden 和 Survivor 區域)回收記憶體被稱為 Minor GC。這一定義既清晰又易於理解。但是,當發生Minor GC事件的時候,有一些有趣的地方需要注意到:

當 JVM 無法為一個新的物件分配空間時會觸發 Minor GC,比如當 Eden 區滿了。所以分配率越高,越頻繁執行 Minor GC。

記憶體池被填滿的時候,其中的內容全部會被複制,指標會從0開始跟蹤空閒記憶體。Eden 和 Survivor 區進行了標記和複製操作,取代了經典的標記、掃描、壓縮、清理操作。所以 Eden 和 Survivor 區不存在記憶體碎片。寫指標總是停留在所使用記憶體池的頂部。
執行 Minor GC 操作時,不會影響到永久代。從永久代到年輕代的引用被當成 GC roots,從年輕代到永久代的引用在標記階段被直接忽略掉。
質疑常規的認知,所有的 Minor GC 都會觸發“全世界的暫停(stop-the-world)”,停止應用程式的執行緒。對於大部分應用程式,停頓導致的延遲都是可以忽略不計的。其中的真相就 是,大部分 Eden 區中的物件都能被認為是垃圾,永遠也不會被複制到 Survivor 區或者老年代空間。如果正好相反,Eden 區大部分新生物件不符合 GC 條件,Minor GC 執行時暫停的時間將會長很多。所以 Minor GC 的情況就相當清楚了——每次 Minor GC 會清理年輕代的記憶體。
Major GC vs Full GC
大家應該注意到,目前,這些術語無論是在 JVM 規範還是在垃圾收集研究論文中都沒有正式的定義。但是我們一看就知道這些在我們已經知道的基礎之上做出的定義是正確的,Minor GC 清理年輕帶記憶體應該被設計得簡單:
Major GC 是清理老年代。
Full GC 是清理整個堆空間—包括年輕代和老年代。

相關推薦

記憶體回收機制初解

【初步學習記憶體回收機制】jvm記憶體的記憶體回收機制主要是針對堆來講的因為棧的記憶體回收機制隨著一個基本型別的建立與銷燬而進行建立而銷燬,比較固定。【如何判斷物件是否死亡】早期有兩種演算法:第一種是【引用計數演算法】:    給物件新增一個計數器,物件被引用,則+1,物件引

java有自己的記憶體回收機制,但為什麼還存在記憶體洩漏的問題?

原文地址:https://www.cnblogs.com/panxuejun/p/5888817.html 1.既然 Java 的垃圾回收機制能夠自動的回收記憶體,怎麼還會出現記憶體洩漏的情況呢? 這個問題,我們需要知道 GC 在什麼時候回收記憶體物件,什麼樣的記憶體物件會被 GC 認為

Redis的記憶體回收機制

Redis的記憶體回收機制 2018年01月16日 17:11:48 chs007chs 閱讀數:1172   Redis的記憶體回收機制主要體現在一下兩

Memcached(記憶體模型、記憶體回收機制

memcached記憶體模型 基本概念page/slab/chunk Chunk屬於slab,在一個slab裡面有多個chunk Slab裡面也有多個page。 1slab class(slab分類) = n slab 1page=1slab = n chunk(統一長度)

Redis 的過期策略以及記憶體回收機制

一、Redis過期策略  redis是如何處理過期的key?      分為2種:passive (被動)和active(主動)      所謂被動的處理方式就是 :當一些客戶端進行訪問的時候,祕鑰被動過期,

Linux記憶體回收機制總結

1.當vma flag中標記VM_LOCKED時,表示這段記憶體不能被回收,要常駐記憶體中.  2.   當ZONE的空閒頁面數大於WMARK_HIGH時,這個ZONE就可認為是balanced(由函式zoen_balanced()判斷)  3.&nb

java的記憶體回收機制

如何確定一個物件是否可以被回收? 1、 引用計數演算法:判斷物件的引用數量 2、 可達性分析演算法:判斷物件到GC Root引用鏈是否可達 ps:c++中解決迴圈引用的方法: 1. 當只剩下最後一個引用的時候需要手動打破迴圈引用釋放物件。2. 當A的生存期超過B的生存

Python學習之旅_01day:變數和常量,變數輸入,縮排,if條件,記憶體回收機制

1.變數和常量1.1 變數是為了儲存程式運算過程中的一些中間結果,為了方便日後呼叫1.2 變數存在一定的描述性,讓大眾一看就知道該變數的用途 1.3書寫方式 變數的命名規則 1. 要具有描述性 2. 變數名只能_,數字,字母組成,不可以是空格或特殊字元(#?<.,¥$*!~) 3. 不能以中文為變數名

【JAVA記憶體回收】Java 記憶體回收機制

下面這個圖,很清楚地說明物件在new的時候是怎樣開闢記憶體空間的 其中物件new出來的,是棧記憶體,變數的開闢是堆記憶體 Java的一個重要優點就是通過垃圾收集器GC (Garbage Collection)自動管理記憶體的回收,程式設計師不需

python的記憶體回收機制即gc模組講解

最後容易造成記憶體問題的通常就是全域性單例、全域性快取、長期存活的物件 引用計數(主要), 標記清除, 分代收集(輔助) 引用計數為0則會被gc回收。標記刪除可以解決迴圈引用的問題。分代:0代--年輕代;1代--中年代;2代--老年代,存活越久被回收的頻率越低。 通過gc機制基本解決記憶體回收的問題。

淺談Chrome V8引擎中的記憶體回收機制

垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收機制來自動管理記憶體。垃圾回收是一把雙刃劍,其好處是可以大幅簡化程式的記憶體管理程式碼,降低程式設計師的負擔,減少因 長時間運轉而帶來的記憶體洩露問題。但使用了垃圾回收即意味著程式設計師將無法掌控記憶體。ECMAScript沒有暴

Java jvm 記憶體回收機制

在Java中,它的記憶體管理包括兩方面:記憶體分配(建立Java物件的時候)和記憶體回收,這兩方面工作都是由JVM自動完成的,降低了Java程式設計師的學習難度,避免了像C/C++直接操作記憶體的危險。但是,也正因為記憶體管理完全由JVM負責,所以也使Java很多程式

深入理解JVM記憶體回收機制(不包含垃圾收集器)

##目錄 - 垃圾回收發生的區域 - 如何判斷物件是否可以被回收 - HotSpot實現 - 垃圾回收演算法 - JVM中使用的垃圾收集演算法 - GC的分類 - 總結 - 參考資料 ## 垃圾回收發生的區域 堆是`java`建立物件的區域(`String`物件在常量池中),也是垃圾回收最多的地方。但是除了

垃圾回收機制(GC) Java記憶體區域及物件

前言   上一篇文章Java記憶體區域及物件講述了Java記憶體執行時的各個部分,其中程式計數器、虛擬機器棧、本地方法棧3個區域隨執行緒生而生,隨執行緒滅而滅,在這幾個區域是不需要過多的考慮回收的問題的,因為方法結束或者執行緒結束時,記憶體自然就跟隨著回收了;而Java堆和方法區則不一樣,一個介面中的多個實

JVM記憶體模型及GC回收機制的相關理解

在面試中我們經常會被問道關於JVM的面試問題。我們來整理下 這篇不錯 這個可以讓你恍然大悟 1 JAVA記憶體模型初體驗 JVM記憶體模型: 1 堆 :物件 2 棧(本地方法棧,虛擬機器棧):引數列表、基本資料型別 3 方法區(包括常量池):類變數、常量、程式碼段(code sege

【達內課程】Android中的GC垃圾回收機制記憶體洩漏

當main()方法執行完,main()方法中的區域性變數都會彈棧,從棧當中銷燬 當左側棧中的e2和e銷燬後,右側中的兩個物件就是垃圾 java底層有一種GC垃圾回收機制,在java程式執行時,GC執行緒會不斷找尋垃圾,是的話會清除掉 當我們點選模擬機的返回鍵時,發生了什麼 當G

Python記憶體與垃圾回收機制

1、python記憶體管理機制和調優手段。   記憶體管理機制:引用計數,垃圾回收、記憶體池。 引用計數:   引用計數是一種非常高效的記憶體管理手段,當一個Python物件被引用時其引用計數增加1,當其不再被一個變數引用時則記數減1,當引用記數等於0時物件被刪除。 垃圾回收:   1、引用計數:

java記憶體結構和垃圾回收機制

java和c語言不同 開發c語言專案,需要程式設計師手動清理垃圾,而java有垃圾自動回收機制。它可以減少記憶體異常發生的機率。 一、java的記憶體結構,可以分為五個區域: 1、程式計數器,它儲存的是程式當前執行的指令的地址,也就是說是用來指示 執行哪條指令的。在多執行緒的環境下,

自動記憶體管理機制(4)- 記憶體分配和回收策略

自動記憶體管理機制(4)- 記憶體分配和回收策略 Java所承諾的自動記憶體管理主要是針對物件記憶體的回收和物件記憶體的分配。 在Java虛擬機器的五塊記憶體空間中,程式計數器、Java虛擬機器棧、本地方法棧記憶體的分配和回收都具有確定性,一般在編譯階段就能確定需要分配的記憶體大小,

自動記憶體管理機制(2)- 記憶體回收和垃圾收集演算法

自動記憶體管理機制(2)- 記憶體回收和垃圾收集演算法 1. 概述 首先思考三個問題: 哪些記憶體需要回收 什麼時候回收 如何回收 程式計數器、虛擬機器棧、本地方法棧是執行緒私有的,因此這幾個區域的記憶體分配和回收都具有確定性(執行緒結束時執行垃圾回