Java中使用newnewarrayanewarray 和 multianewarray 指令來創建的對象當這些對象不再使用時由垃圾收集來釋放那麼 反序列化等都是間接使用了前面的某個指令 clone() 是個本地方法?
JVM規范不需要任何特定的垃圾收集技術甚至也沒要求有垃圾收集機制大概只是說不需要手工釋放內存具體怎麼實現各 JVM 自行決定
GC 除了釋放不再被引用的對象還要處理堆碎片整理出連續的空閒空間才能放得下新的對象不至於出現總的空閒空間足夠但碎片太多而報出 Out of Memory 的異常
GC有兩個好處一個是提高的生產率不用埋頭於 Memory Link 的有時甚至是逐行的檢查二GC 也是 Java 安全策略的一部分有了它不至於因錯誤的釋放內存而導至 JVM 崩潰但是 GC 的一個潛在缺陷影響了程序的性能它需要一直在後台不時的做些事情而且實時性也有所欠缺
垃圾收集算法
GC算法要做兩件基本的事情 檢測出垃圾對象 回收垃圾對象釋放相應堆空間垃圾檢測一般是先建立一個根對象集合其他對象要是從根對象起可觸及就是活的無法到達的就是垃圾這裡的根對象的認定就有些講究的不同的 JVM 的看法不完全一致但總是會包含局部變量中的對象引用和棧幀的操作數棧(以及類變量中的引用)
另一個根對象的來源是被加載的類的常量池中的對象引用類的常量池中的字符串包括有類名超類名超接口名字段名字段特征簽名方法名方法特征簽名
還有一個來源是傳遞到方法中的沒有被本地方法釋放的對象引用(根據本地方法接口本地方法可以通過簡單的返回來釋放引用或者顯式的調用一個回調函數來釋放傳遞來的引用或是這兩者的結合)
再一個潛在的根對象來源是JVM 運行時數據區中從垃圾收集器的堆中分配的部分某些實現中方法區中的類數據本身可能被存放在使用垃圾收集器的堆中以便使用和釋放對象同樣的垃圾收集算法來檢測和卸載不再被引用的類
在某些 JVM 實現中像基本類型如一個 int 如果被解釋為一個本地指針那它就是指向堆中的一個對象但是保守的垃圾收集器對這種基本類型引用的堆中的對象不處理
區別活動對象和垃圾的兩個基本方法是引用計數和跟蹤
引用計數收集器
引用計數是垃圾收集的早期策略這種方法時堆中的每個對象都有一個引用計數當對象創建並賦給一個變量時引用計數為 每次賦給別的變量時引用計數加 當對象的引用超過了生存期或指向到了新值(如果引用置為 null)對象的引用計減 這樣對象的引用計數為 時就是垃圾可清除的引用計數對多個對象的循環引用無能為力其實這一存對象都是死的但引用計數都不為 還有引用數的增減帶來額外開銷基於這些缺陷這種技術現在已經不為人所接受了
跟蹤收集器
跟蹤收集成追蹤從根節點開始的對象引用圖基本的追蹤算法叫作標記並清除也就是垃圾收集的兩個階段標記階段垃圾收集器遍歷引用數標記每一個遇到的對象清除階段未被標記的對象被釋放可能在對象本身設置標記要麼就是用一個獨立的位圖來設置標記
壓縮收集器
垃圾收集同時要應對碎片整理的任務標記和清除通常使用兩種策略來消除堆碎片壓縮和拷貝這兩種方法都是快速移動對象來減小碎片
壓縮收集我想應該是在標記清除之後來做的?壓縮收集器把活動對象越過空閒區滑到堆的一堆留下另一端的大的連續空閒塊被移動的對象的引用也被更新指向新的位置更新被移動對象的引用有時通過一個間接對象引用層來實現的對象的引用不實際指向堆中對象而是指向一個對象句柄表(由它完成對象引用到堆中對象的實際位置的映射)對象被移動了只需要更新對象句柄表的句柄值這樣程序中的對象引用不變這種方法簡化了消除堆碎片的工作但是每次對象訪問都要查一下映射表帶來了性能上的損失
拷貝收集器
拷貝收集器把所有的活動對象移動到一個新的區域這種方法在追蹤對象過程中隨著發現而被拷貝不再有標記和清除的區分一般的拷貝收集算法稱為停止並拷貝在這個方案中堆被分為兩個區域任何時候只使用其中一個區域對象在某一個區域中分配直到這個區域被耗盡時程序執行停止遍歷這個區域活動對象移到另一個區域完成後程序恢復執行對象在新的區域分配原來的區域剩下垃圾全清除直到新的區域耗盡時程序停止活動對象又往回移循環工作這種方法的代價就是堆內存只能使用到一半
下面是停止和拷貝算法的垃圾收集過程中以時間為線索的 個快照
看過這個圖應該不用多加解釋反正就是堆分成上下兩個區域哪部分滿了活動對象往另一部分跑被移出的區域就成空的了來來回回新對象總是在正用的那部份分配想想你在運行 Java 程序的時候應該有過突然被中止不動的時候可能就是 GC 在活動了
分代收集器
簡單的停止拷貝收集器的缺點是每次收集時所有的活動對象都要移動來移動去對於短生命的對象還好說經常可以就地解決掉可是對於長生命周期的對象就純粹是個體力勞動了把它挪來挪去除消耗大量的時間沒有產生任何效益分代收集能直接讓長生命周期的對象長時間的呆在一個地方按兵不動GC 的精力可以更多的花在收集短命對象上
這種方法裡堆被分成兩個或更多的子堆每一個堆為一代對象服務最年幼的那一代進行最頻繁的垃圾收集因為多數對象是短命的只有很小部分的年幼對象可以在經歷第一次收集後還存活如果一個最年幼的對象經歷了好幾次垃圾收集後仍是活著的那這個對象就成為壽命更高的一代它被轉移到另外一個子堆中去年齡更高一代的收集沒有年輕一代來得頻繁每當對象在所屬的年齡代中變得成熟(多次垃圾收集後仍幸存)之後就可以轉移到更高年齡的一代中去
分代收集除了可應用於拷貝算法也可以應用於標記清除算法不管在哪種情況下把堆按照對象年齡分組可以提高最基本的垃圾收集的性能
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26335.html