JVM 新生代垃圾回收时,如何避免对整个堆进行扫描来找出存活对象?
考察说明
考察对JVM分代收集与卡表(Card Table)机制的理解。
回答思路
- 【回答框架 1】JVM采用分代收集,将堆划分为新生代和老年代。避免全堆扫描的关键是使用卡表(Card Table)记录老年代对象对新生代对象的引用,即记忆集(Remembered Set)的一种实现。
- 【回答框架 2】卡表是一个字节数组,每个字节对应老年代的一个区域(通常512字节),当老年代对象引用新生代对象时,该区域在卡表中被标记为脏(Dirty)。
- 【回答框架 3】在新生代GC(如Minor GC)时,只扫描卡表中标记为脏的老年代区域,作为GC Roots的一部分,而无需扫描整个老年代,从而避免全堆扫描。
- 【回答框架 4】卡表通过写屏障(Write Barrier)维护,即在老年代引用更新时,将对应卡标记为脏。以此在时间与空间开销之间取得平衡。
- 【关键点 1】卡表以字节数组实现,通过写屏障维护脏标记。
- 【关键点 2】新生代GC仅扫描脏卡对应的老年代区域作为GC Roots。
- 【关键点 3】使用卡表而非全堆扫描,降低GC停顿时间。
- 【关键点 4】卡表只能记录引用关系,不能替代可达性分析本身。
- 【易错点 1】卡表只是记录引用位置,不保证引用一定存活,仍需可达性分析。
- 【易错点 2】写屏障有额外开销,需权衡精度与性能。
- 【易错点 3】不要误以为卡表能完全避免扫描老年代,只避免全量扫描。