註意:本文主要參考自《深入理解Java虛擬機(第二版)》 說明:查看本文之前,推薦先知道JVM記憶體結構,見《第一章 JVM記憶體結構》 1、記憶體回收的區域 堆:這是GC的主要區域 方法區:回收兩樣東西 無用的類 廢棄的常量 棧和PC寄存器是線程私有區域,不發生GC 2、怎樣判斷對象是否存活 垃圾回收:
註意:本文主要參考自《深入理解Java虛擬機(第二版)》
說明:查看本文之前,推薦先知道JVM記憶體結構,見《第一章 JVM記憶體結構》
1、記憶體回收的區域
- 堆:這是GC的主要區域
- 方法區:回收兩樣東西
- 無用的類
- 廢棄的常量
- 棧和PC寄存器是線程私有區域,不發生GC
2、怎樣判斷對象是否存活
垃圾回收:回收掉死亡對象所占的記憶體。判斷對象是否死亡,有兩種方式:
- 引用計數法
- 原理:給對象添加一個引用計數器,每當有一個地方引用它時,計數器值+1;引用失效時,計數器值-1
- 實際中不用,不用的兩個原因
- 每次為對象賦值時,都要進行計數器值的增減,消耗較大
- 對於A、B相互引用這種情況處理不了(這一點是不用的主要原因)
- 可達性分析(跟蹤收集)
- 原理:從根集合(GC Roots)開始向下掃描,根集合中的節點可以到達的節點就是存活節點,根集合中的節點到達不了的節點就是將要被回收的死亡節點,如下圖中的A/B/C是存活節點,D/E是死亡節點:
-
- 根集合中的節點包括:簡單來講,就是全局性的引用(常量和靜態屬性)和棧引用(下邊第一、三)
- Java棧中的對象引用(存在於局部變數表中,註意:局部變數表中存放的是基本數據類型和對象引用)
- 這是垃圾回收最多考慮的地方,所以有時,我們也會將死亡對象稱為"沒有引用指向的對象"
- 方法區中:常量+靜態(static)變數
- 傳到本地方法中,還沒有被本地方法釋放的對象引用
- Java棧中的對象引用(存在於局部變數表中,註意:局部變數表中存放的是基本數據類型和對象引用)
- 根集合中的節點包括:簡單來講,就是全局性的引用(常量和靜態屬性)和棧引用(下邊第一、三)
3、3種引用類型
- 強引用(Strong Reference):A a = new A();//a是強引用
- 軟引用(Soft Reference):當記憶體不足時,釋放軟引用所引用的對象;當記憶體足夠時,就是一個普通對象(強引用)
- 弱引用(Weak Reference):弱引用對象只能存活到下一次垃圾回收之前,一旦發生垃圾回收,立刻被回收掉
4、方法區的回收
- 廢棄常量:例如,沒有任何一個引用指向常量池中的"abc"字元串,則"abc"字元串被回收
- 無用的類:滿足以下三個條件
- Java堆中不存在該類的任何實例
- 載入該類的ClassLoader被回收
- 該類的Class對象沒有在任何地方被引用
註意:
- 在實際開發中,儘量不用JSP去做前端,而是用velocity、freemarker這樣的模板引擎去做
- 與類相關常用的三個參數:
- -XX:+PrintClassHistogram:輸出類統計狀態
- -XX:-TraceClassLoading:列印類載入信息
- -XX:-TraceClassUnloading:列印類卸載信息 View Code
5、垃圾回收線程
系統的垃圾回收是由垃圾回收線程來檢測操作的,該線程是一個後臺線程(daemon thread)。
5.1、後臺線程與我們使用的前臺線程而言,有一個特點:當JVM中的前臺線程數量為0時,後臺線程自動消亡。可以這樣講,後臺線程依托於前臺線程而存在。
5.2、垃圾回收線程為什麼要設置成為後臺線程呢?
我們想一下,當前臺一個線程都沒有時,垃圾還會有嗎?或者說垃圾回收還有必要嗎?答案是沒有必要,所以此時垃圾回收線程也就失去了存活的意義。
所以可以這樣講,將一個線程是否設置為後臺線程,就看這條線程在沒有其他線程存在的情況下,是否還有存活的意義。
例如,在我們使用Apache mina2做RPC時,我們在消息的接收端直接開啟一個後臺線程啟動服務來接受消息發送端發來的消息事件請求就可以。試著去想,如果在整個JVM中只有當前的這一個後臺線程了,那麼這個線程還有必要存活下來嗎?當然沒有必要,因為消息永遠都不會再發送了(前臺線程都沒了)