jvm的卡表(Card Table)

2023-10-11 13:50
文章标签 java jvm table card 卡表

本文主要是介绍jvm的卡表(Card Table),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载自:https://juejin.im/post/5c39920b6fb9a049e82bbf94

 

JVM之卡表(Card Table)

我们知道,JVM在进行垃圾收集时,需要先标记所有可达对象,然后再清除不可达对象,释放内存空间。那么,如何快速的找到所有可达对象呢?

最简单粗暴的实现,就是每次进行垃圾收集时,都对整个堆中的所有对象进行扫描,找到所有存活对象。逻辑是简单,但性能比较差。

简单粗暴的实现方式,通常都是不可取的。那JVM是如何实现快速标记可达对象的?

答案是GC Roots。

GC Roots是垃圾收集器寻找可达对象的起点,通过这些起始引用,可以快速的遍历出存活对象。GC Roots最常见的是静态引用和堆栈的局部引用变量。然而,这不是我们这讲的重点:)

现代JVM,堆空间通常被划分为新生代和老年代。由于新生代的垃圾收集通常很频繁,如果老年代对象引用了新生代的对象,那么,需要跟踪从老年代到新生代的所有引用,从而避免每次YGC时扫描整个老年代,减少开销。

对于HotSpot JVM,使用了卡标记(Card Marking)技术来解决老年代到新生代的引用问题。具体是,使用卡表(Card Table)和写屏障(Write Barrier)来进行标记并加快对GC Roots的扫描。

卡表(Card Table)

基于卡表(Card Table)的设计,通常将堆空间划分为一系列2次幂大小的卡页(Card Page)。

卡表(Card Table),用于标记卡页的状态,每个卡表项对应一个卡页。

HotSpot JVM的卡页(Card Page)大小为512字节,卡表(Card Table)被实现为一个简单的字节数组,即卡表的每个标记项为1个字节。

当对一个对象引用进行写操作时(对象引用改变),写屏障逻辑将会标记对象所在的卡页为dirty。

OpenJDK/Oracle 1.6/1.7/1.8 JVM默认的卡标记简化逻辑如下:

CARD_TABLE [this address >> 9] = 0;
复制代码

首先,计算对象引用所在卡页的卡表索引号。将地址右移9位,相当于用地址除以512(2的9次方)。可以这么理解,假设卡表卡页的起始地址为0,那么卡表项0、1、2对应的卡页起始地址分别为0、512、1024(卡表项索引号乘以卡页512字节)。

其次,通过卡表索引号,设置对应卡标识为dirty。

 

 

 

带来的2个问题

1.无条件写屏障带来的性能开销

每次对引用的更新,无论是否更新了老年代对新生代对象的引用,都会进行一次写屏障操作。显然,这会增加一些额外的开销。但是,与YGC时扫描整个老年代相比较,这个开销就低得多了。

不过,在高并发环境下,写屏障又带来了虚共享(false sharing)问题。

2.高并发下虚共享带来的性能开销

在高并发情况下,频繁的写屏障很容易发生虚共享(false sharing),从而带来性能开销。

假设CPU缓存行大小为64字节,由于一个卡表项占1个字节,这意味着,64个卡表项将共享同一个缓存行。

HotSpot每个卡页为512字节,那么一个缓存行将对应64个卡页一共64*512=32KB。

如果不同线程对对象引用的更新操作,恰好位于同一个32KB区域内,这将导致同时更新卡表的同一个缓存行,从而造成缓存行的写回、无效化或者同步操作,间接影响程序性能。

一个简单的解决方案,就是不采用无条件的写屏障,而是先检查卡表标记,只有当该卡表项未被标记过才将其标记为dirty。

这就是JDK 7中引入的解决方法,引入了一个新的JVM参数-XX:+UseCondCardMark,在执行写屏障之前,先简单的做一下判断。如果卡页已被标识过,则不再进行标识。

简单理解如下:

if (CARD_TABLE [this address >> 9] != 0)CARD_TABLE [this address >> 9] = 0;
复制代码

与原来的实现相比,只是简单的增加了一个判断操作。

虽然开启-XX:+UseCondCardMark之后多了一些判断开销,但是却可以避免在高并发情况下可能发生的并发写卡表问题。通过减少并发写操作,进而避免出现虚共享问题(false sharing)。

也用于CMS GC

CMS在并发标记阶段,应用线程和GC线程是并发执行的,因此可能产生新的对象或对象关系发生变化,例如:

  • 新生代的对象晋升到老年代;
  • 直接在老年代分配对象;
  • 老年代对象的引用关系发生变更;
  • 等等。

对于这些对象,需要重新标记以防止被遗漏。为了提高重新标记的效率,并发标记阶段会把这些发生变化的对象所在的Card标识为Dirty,这样后续阶段就只需要扫描这些Dirty Card的对象,从而避免扫描整个老年代。

参见:Java之CMS GC的7个阶段

这篇关于jvm的卡表(Card Table)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/188502

相关文章

Java堆转储文件之1.6G大文件处理完整指南

《Java堆转储文件之1.6G大文件处理完整指南》堆转储文件是优化、分析内存消耗的重要工具,:本文主要介绍Java堆转储文件之1.6G大文件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可... 目录前言文件为什么这么大?如何处理这个文件?分析文件内容(推荐)删除文件(如果不需要)查看错误来源如何避

SpringBoot整合Dubbo+ZK注册失败的坑及解决

《SpringBoot整合Dubbo+ZK注册失败的坑及解决》使用Dubbo框架时,需在公共pom添加依赖,启动类加@EnableDubbo,实现类用@DubboService替代@Service,配... 目录1.先看下公共的pom(maven创建的pom工程)2.启动类上加@EnableDubbo3.实

SpringBoot整合(ES)ElasticSearch7.8实践

《SpringBoot整合(ES)ElasticSearch7.8实践》本文详细介绍了SpringBoot整合ElasticSearch7.8的教程,涵盖依赖添加、客户端初始化、索引创建与获取、批量插... 目录SpringBoot整合ElasticSearch7.8添加依赖初始化创建SpringBoot项

JAVA覆盖和重写的区别及说明

《JAVA覆盖和重写的区别及说明》非静态方法的覆盖即重写,具有多态性;静态方法无法被覆盖,但可被重写(仅通过类名调用),二者区别在于绑定时机与引用类型关联性... 目录Java覆盖和重写的区别经常听到两种话认真读完上面两份代码JAVA覆盖和重写的区别经常听到两种话1.覆盖=重写。2.静态方法可andro

SpringBoot中六种批量更新Mysql的方式效率对比分析

《SpringBoot中六种批量更新Mysql的方式效率对比分析》文章比较了MySQL大数据量批量更新的多种方法,指出REPLACEINTO和ONDUPLICATEKEY效率最高但存在数据风险,MyB... 目录效率比较测试结构数据库初始化测试数据批量修改方案第一种 for第二种 case when第三种

解决1093 - You can‘t specify target table报错问题及原因分析

《解决1093-Youcan‘tspecifytargettable报错问题及原因分析》MySQL1093错误因UPDATE/DELETE语句的FROM子句直接引用目标表或嵌套子查询导致,... 目录报js错原因分析具体原因解决办法方法一:使用临时表方法二:使用JOIN方法三:使用EXISTS示例总结报错原

Java docx4j高效处理Word文档的实战指南

《Javadocx4j高效处理Word文档的实战指南》对于需要在Java应用程序中生成、修改或处理Word文档的开发者来说,docx4j是一个强大而专业的选择,下面我们就来看看docx4j的具体使用... 目录引言一、环境准备与基础配置1.1 Maven依赖配置1.2 初始化测试类二、增强版文档操作示例2.

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完