淺析Java內存區及其垃圾回收機制
- Java三大內存區
- Java堆與棧的形象描述
- 兩種垃圾判別機制
- 兩種垃圾回收技術
- JIT編譯器技術簡介
Java三大內存區
Java堆與棧的形象描述
堆
Java的堆比較特殊,《Java編程思想》(第4版)裏把Java的堆(Java的堆是堆,Java的堆棧是棧)比作一個傳送帶,每分配一個新對象,它就往前移動一格。
但是其實這樣的描述不夠準確,因爲這樣的話會造成大量的空間浪費。我們可以設想,如果JVM真的像傳送帶一樣實現堆,那麼即使一個對象不在被使用,其存儲空間也不會被釋放。我們可以這樣理解Java的堆:
C++的堆無法抽象成棧主要是因爲其沒有獨立於用戶程序的垃圾回收機制,每一塊分配出去的存儲實例對象的內存都要由其自身管理(包括銷燬)。這也就是說難以實現對C++堆的整體內存再分配,所以如果把C++的結構抽象爲棧這一數據結構的話,那麼很難保證C++的堆不會滿。而Java則不同了,其具有一個自動的,面向於JVM的垃圾回收機制(相當於MySQL 裏的OPTIMIZE TABLE對錶的作用);通過GC,那麼我們可以利用虛擬機自動實現棧中空間的重新整理,並對Java棧中這些實例對象的引用變量的值進行自動更新(這也是Java引用與c的指針的區別之一)。
棧
下面說一下兩種垃圾判別機制
引用計數
特點 | 描述 |
---|---|
發生時間 | 程序生命週期。 |
方式 | 每個對象都有一個引用計數器,當引用離開作用域或被置爲null時減一,當對象被連接到一個新引用變量上時,計數器加1。當對象引用計數器爲0時,釋放對象。 |
優點 | 思想簡單。 |
缺點 | 當出現循環引用的情況時,無法及時回收相關對象;且JVM判斷循環引用的開銷較大。 |
可以使用冒號來定義對齊方式:
追溯堆棧或靜態存儲區活引用法
這種方法,就是從堆棧和靜態存儲區開始,遍歷所有引用,對於發現的每個對象,再遍歷此對象所包含的所有引用,反覆進行。類似於圖的遍歷,在這個圖裏的所有節點(對象)都是活的,剩餘的對象就可以被判別爲回收了。而且,因爲“引用計數”法中的交互引用對象幾乎不會在堆棧和靜態存儲區中(而是隨着對象存儲在堆中)出現,所以幾乎完全避免了這個現象。
兩種垃圾回收技術
總括 《Java編程思想》(第四版中文版)關於JVM垃圾回收技術的概括:
自適應的、分代的、停止-複製、標記-清掃的。
我們先來介紹下停止-複製技術的步驟:
1. 暫停程序運行(這點看出其不屬於後臺回收模式);
2. 將堆中存活的對象從當前堆(或者是當前堆中的塊),複製到另一個可用堆(或者是當前堆中可用的塊)中;
3. 原來的堆(塊)中就只剩下垃圾,交給GC處理。
注意:
於是,引出了標記-清掃方式
1. JVM從靜態存儲區和棧開始遍歷所有引用,標記其中所有獲得引用;
2. 之後,標記工作結束時,JVM回收沒有被標記的對象。但是,不發生對象複製,所以會節省空間和時間,但並沒有起到使對象緊湊排列的作用。
自適應
JVM會監視垃圾回收效率,選擇並切換這兩種方式進行垃圾回收。
分代
Java虛擬機的分代比較複雜,可以參考這篇文章。