## 記憶集與卡表、寫屏障
### 問題一:對象跨代(區域)引用,GC Roots掃描范圍如何界定?
當部分區域進行垃圾收集時,如果非收集區域的對象跨區引用了收集區域的對象(收集區域的對象A可能被非收集區域的對象靜態字段B引用,這樣對象A應加入GC Roots集合,防止被錯誤清理),雖然可以籠統的將其他所有區域都加入到GC Roots的掃描范圍,可如此勢必會有額外的性能消耗。
因此,HotSpot加入了記憶集(Remembered Set)來避免掃描范圍過大帶來的性能消耗。
### 問題二:記憶集是什么?
記憶集是一種用于記錄非收集區域指向收集區域的指針集合的抽象數據結構。
如果不考慮成本和效率的話,最簡單的實現可以用非收集區域中所有含跨代(區域)引用的對象數組來實現這個數據結構。
### 問題三:記憶集實現精度?
有3種實現精度,分別如下:
1. 字長精度:每個記錄精確到一個機器字長(JVM地址),表示該字包含跨代指針。
2. 對象精度:每個記錄精確到一個對象,表示該對象里有字段包含有跨代指針。
3. 卡精度:每個記錄精確到一塊內存區域,表示該區域內有對象包含有跨代指針。
### 問題四:HotSpot虛擬機記憶集的實現時什么?
HotSpot虛擬機用一個字節數組來實現卡精度,也叫做卡表的方式實現記憶集。
卡表(card table)的每一個元素對應內存區域中一塊特定的內存塊,叫做卡頁(card page)。
一般來說卡頁大小都是2的n次冪,HotSpot中卡頁大小為2的9次冪,即512字節。地址右移9位,得到的是卡表的索引序號。一個卡頁的內存中通用有多個對象,但是只有卡頁中有一個對象的字段存在著跨代指針,則將該卡頁對應的卡表索引元素值標識為1(稱為變臟),沒有則標識為0。在垃圾收集發送時,只要將卡表中標識為1的對應的卡頁內存塊加入到GC Roots中一并掃描。
### 問題五:卡片元素何時變臟?
有其他分代區域中的對象引用了本區域對象時,在引用字段類型賦值的時刻將其(引用字段)對應的卡表元素變臟。
HotSpot虛擬機通過**寫屏障Write Barrier**技術來維護卡表狀態。(這里的寫屏障和內存屏障無關)
### 問題六:寫屏障是什么?
寫屏障可以看著時虛擬機層面對“引用類型指端賦值”這個動作的AOP切面。在引用對象賦值是會產生一個環形(Around)通知,供程序執行額外動作。賦值前的寫屏障叫寫前屏障,賦值后的寫屏障叫寫后屏障。
### 問題七:卡表更新偽共享問題如何解決?
HotSpot虛擬機應用寫屏障后,就會為所有賦值操作生成相應的指令,一旦收集器在寫屏障中新增了更新卡表操作,就會執行卡表更新操作。而處理器緩存行多為64字節,且一個卡表元素占一個字節,所以64個卡表元素共用同一個緩存行。這64個卡表元素對應的卡頁大小為32KB(64x512字節=64x2^9),也就是說如果不同線程更新的對象正好在這32KB內存區域內,就會導致更新卡表時正好寫入同一個緩存行而影響性能。
為了避免偽共享問題,JDK7后HotSpot增加了`-XX:+UseCondCardMark`參數來開啟卡表更新時判斷卡表標記為過時,未過時才將其標記記為變臟。
- 面試突擊
- Java虛擬機
- 認識字節碼
- 000Java發展歷史
- 000Macos10.15.7上編譯OpenJDK8u
- 001熟悉Java內存區域
- 002熟悉HotSpot中的對象
- 003Java如何計算對象大小
- 004垃圾判定算法與4大引用
- 005回收堆和方法區中對象
- 006垃圾收集算法
- 007HotSpot虛擬機垃圾算法實現篇1
- 007HotSpot虛擬機垃圾算法實現篇2
- 007HotSpot虛擬機垃圾算法實現篇3
- 008垃圾收集器
- 009內存分配與回收策略
- 010Java虛擬機相關工具
- 011調優案例分析
- 012一次IDEA的啟動速度調優
- 013類文件Class的結構
- 014熟悉字節碼指令
- 015類加載機制(過程)
- 016類加載器
- IDEA的JVM參數
- Java基礎
- Java自動裝箱與拆箱
- Java基礎數據類型
- Java方法的參數傳遞
- Java并發
- 001走入并行的世界
- 002并行程序基礎
- 003熟悉Java內存模型JMM
- 004Java并發之volatile關鍵字
- 005線程池入門到精通
- 006Java多線程間的同步控制方法
- 007Java維基準測試框架JMH
- 008Java并發容器
- 009Java的線程實現
- 010Java關鍵字synchronized
- 011一些并行模式的熟悉
- 單例模式和不變模式
- 生產者消費者模式
- Future模式
- 012一些并行算法的熟悉
- 面試總結
- 長亮一面