JVM(三) 垃圾收集器與記憶體分配策略
一 重點關注的資料區域: 堆 和 方法區
Java記憶體執行時區域中的程式計數器、虛擬機器棧、本地方法棧3個區域隨執行緒生,隨執行緒滅;每 一個棧幀中分配多少記憶體是在類結構確定下來就已知的,因此這幾個區域的記憶體分配和回收都 具備確定性.
二 垃圾回收之如何判定物件已死?
1 引用計數演算法
給物件新增一個引用計數器,沒當一個地方引用它時,計數器就加1;當引用失效 時,計數器就減1.任何計數器為0的物件就是不能再被使用的.
優點:簡單高效 缺點:對互相引用的物件無效
2 可達性分析演算法
通過一系列的成為“GC Roots”的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈。如果在一次的搜尋中,一個物件到GC Roots沒有任何的引用鏈相連,則說明此物件是不可用的。具體如圖所示
GC Roots的判定: 1、虛擬機器棧(棧幀中的本地變量表)中引用的物件 2、方法區中靜態屬性引用的物件 3、方法區中常量引用的物件 4、本地方法棧中JNI(即一般說的Native方法)引用的物件
三 垃圾回收之如何判定引用無效?
通過前面的分析發現,無論是引用計數還是可達性分析,判定物件的生死都與“引用”有關. 老版本的引用定義很狹隘,現在又進行了擴充:強引用、軟引用、弱引用、虛引用.
1、當JVM進行垃圾收集時,JVM使用可達性分析演算法進行分析,如果物件在進行可達性分析後發現沒有與GC Roots相連線的引用鏈,此時該物件將被第一次標記,並進行一次篩選,篩選的條件是此物件有沒有必要執行finalize()方法,如果物件沒有覆蓋該方法,或者該方法已經被虛擬機器呼叫過了,虛擬機器將這兩種情況都視為“沒有必要執行”。 2、如果該物件被判定為有必要執行finalize()方法,那麼物件將會被放置到一個叫做F-Queue的佇列中,並在稍後由一個由虛擬機器自動建立的、低優先順序的Finalizer執行緒去執行它。這裡所謂的執行是指虛擬機器會觸發這個方法,但並不承若會等待它執行結束。因為一個物件可能在finalize()方法中執行緩慢,或者發生了死迴圈,這將導致該佇列中的其他物件長期處於等待階段,甚至導致整個記憶體系統的奔潰。 3、F-Queue中的標記篩選。 finalize()方法是物件逃脫死亡命運的最後一次機會,然後GC將對F-Queue中的物件進行第二次小規模的標記。如果物件在finalize()方法中成功拯救了自己,即與引用鏈上的任何一個物件建立關聯,那麼在第二次標記的時候,該演算法將被移出F-Queue的集合,如果物件這個時候還沒有逃脫,那基本上它就真的被回收了。
四 垃圾回收之回收方法區
略
五 垃圾回收之回收演算法
分類 | 標記-清除演算法 | 複製演算法 | 標記整理演算法 |
---|---|---|---|
是否整理 | 否 | 是 | 是 |
演算法實現過程 | |||
優點 | |||
缺點 |
StringBuffer通過三種構造方法建立的物件初始容量都不相同,無引數的構造方法初始容量是16個字元,int size引數的 構造方法初始容量是size指定的字元個數,而String s引數的構造方法建立的物件初始容量是S的長度額外加16個字元。通過原始碼得知看出無論哪種構造方法建立物件,當物件呼叫append()方法拼接字串時,如果超出了預先設定的容量,會更新容量擴大為2倍+2,如果仍然不夠,會預設為拼接字串長度加上原空間大小。 因為String作為charl型別的陣列存在一經確定記憶體中不可以改變長度,每次拼接都會在記憶體中開闢一個新的記憶體空間存放,因此效率太慢引如StringBuffer,但是StringBuffer在拼接時如果超出了預先設定的空間擴容時會重新在記憶體中開闢新的空間嗎???我列印雜湊值,前後是相同的,但是查閱一些書本卻說如果本身容量足夠,拼接的時候不會建立新的記憶體空間,擴容時會開闢新的記憶體空間。