写屏障
卡表在产生跨代引用时变脏,Java虚拟机通过写屏障的方式来实现这个变脏的过程,即:在虚拟机层面通过AOP对“引用类型字段赋值”操作插入代码,在操作前的代码叫做“写前屏障”,操作后的代码叫做“写后屏障”,维护卡表使用的是“写后屏障”。
voidoop_field_store(oop*field,oopnew_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
开启会增加一次额外判断的开销,但可以避免伪共享问题。