小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
写屏障
卡表在产生跨代引用时变脏,Java虚拟机通过写屏障的方式来实现这个变脏的过程,即:在虚拟机层面通过AOP对“引用类型字段赋值”操作插入代码,在操作前的代码叫做“写前屏障”,操作后的代码叫做“写后屏障”,维护卡表使用的是“写后屏障”。
void oop_field_store(oop* field, oop new_value) {
// 引用字段赋值
*field = new_value;
// 写后屏障完成卡表状态更新
post_write_barrier(field, new_value);
}
写后屏障维护卡表是有额外开销的,但相比于在Minor GC阶段就扫描整个老年代而言这个代价相对较低。
伪共享问题
由于处理器的缓存系统是缓存行为单位存储,当多线程修改相互独立变量而这些变量有在同一个缓存行,就会影响性能。例如缓存行为64Byte,一个卡表元素占用一个Byte,那么64个卡表元素共享一个缓存行,这64个卡表元素对应了一块32KB的内存,如果不同线程同时对其更新就会出现伪共享问题,解决方法为先检查卡表的标记,只有该值没有被标记过才进行处理。
if (CARD_TABLE[this >> address] >> 9 != 1) {
CARD_TABLE[this >> address] >> 9 = 1;
}
在JDK7后可以自行决定是否要开启卡表更新的条件判断,参数为
-XX: +UseCondCardMark
开启会增加一次额外判断的开销,但可以避免伪共享问题。