2023.9.3 三色标记、Shenandoah和ZGC收集器、Redis消息队列

周末生病去了趟医院,于是把周六和周日的记录放在今天一起讲。周末不用上课就是好,一觉睡到十点钟,回家去看了一下病,刚好周六刮台风,跑高速真的哈人,十米的路都看不清,回学校继续做黑马点评。学了Redis的消息队列,感觉还是RabbitMQ好用一点,写起来也方便。周日一觉睡到下午,刷了算法继续看了《深入JAVA虚拟机》,总结还是跟以往一样先讲力扣吧。


写了两道,都是跟栈有关的

第一道,写一个栈,并且提供一个方法能获取栈中最小值,这题其实挺简单的,但是下午刚起床脑子有点笨。获取最小值的方法一开始我竟然把全部元素取出来比较,太蠢了,这是栈中的一个方法,如果取完,那么其他方法将会崩溃。后面又写了一种解法,就是在获取最小元素的方法里复制给另外一个栈,再全部取出比较,毫无疑问,数据大的时候会崩溃。最后还是直接选择维护一个临时栈,用来存放正式栈中的最小元素,代码如下。

第二道,如题。整体实现起来还是有点绕的,这里就直接看代码吧,最主要是当字符串为"]"的情况,把括号里的字符串编码之后,要与栈中的元素合并,栈中的元素其实该元素之前的结果。


下午继续看《深入理解JAVA虚拟机》,在看今天的内容的时候忽然看到一个熟悉的名词"三色标记",曾经被一个大佬拷打过这个问题,当时被问懵了。一看,这个内容属于HotSpot的算法细节实现,作者建议跳过先不看,我当时就跳过了,现在回来看一遍三色标记,简单讲一下我的理解。

三色标记法:三色标记作为工具辅助推导将对象分为三个类型,白色:表示对象尚未被垃圾收集器访问过;黑色:表示对象已经被垃圾收集器访问过,并且这个对象的所有引用都扫描过;灰色:表示对象已经被垃圾收集器访问过,但是至少存在一个引用还没被访问过。但是在并发可达性分析的情况下可能会出现下面这两种情况:

(1)赋值器插入了一条或多条从黑色对象到白色对象的新引用;

(2)赋值器删除了全部从灰色对象到该白色对象的直接或间接引用。

对此也分别有两种解决方法:

(1)增量更新要破坏的是第一个条件,当黑色对象插入新的指向白色对象的引用关系时,就将这个新插入的引用记录下来,等并发扫描结束之后,再将这些记录过的引用关系中的黑色对象为根,重新扫描一次。这可以简化理解为,黑色对象一旦新插入了指向白色对象的引用之后,它就变回灰色对象了;

(2)原始快照要破坏的是第二个条件,当灰色对象要删除指向白色对象的引用关系时,就将这个要删除的引用记录下来,在并发扫描结束之后,再将这些记录过的引用关系中的灰色对象为根,重新扫描一次。这也可以简化理解为,无论引用关系删除与否,都会按照刚刚开始扫描那一刻的对象图快照来进行搜索。


继续讲今天看的内容,主要看了两个垃圾收集器,不得不说内容是真的多啊,一个一个来吧。

Shenandoah收集器:

这个收集器也挺有意思的,它不是由Oracle领导开发的,所以只能在OpenJDK里面支持,但是它也使得一些无法升级JDK版本的应用能够享受到垃圾收集器技术发展的最前沿成果。书上内容非常多,这里我只讲一些我觉得比较好理解的(简称S)。

1、S与G1一样,也是采用Region的布局,默认回收策略也是优先处理回收价值最大的Region,但是S能够与用户线程并发执行整理算法,S同时也摒弃了G1中要耗费大量内存和资源去维护的记忆集,而是改用了"连接矩阵",这个全局数据结构可以跨Region来记录引用关系;

2、S收集器的垃圾回收可以被分为九个阶段,这里只讲最重要的三个阶段:

(1)并发标记,这个阶段前后还有初始标记和最终标记,这三个操作我愿称之为"标记三兄弟",在CMS、G1以及一会要说的ZGC上都有;

(2)并发回收,其实就是标记-复制,之前的收集器也有这样做,但是S是与用户线程并发执行的,实践起来会存在冲突问题,S通过读屏障和"Brooks Pointers"转发指针来解决,这部份内容在三色标记那一章里,回头再细说;

(3)并发引用更新,真正开始进行引用更新操作,也是与用户线程并发执行的,它只需要按照内存物理地址的顺序,线性地搜索出引用类型,再把新值改为旧值即可,而无需像之前的操作一样根据对象图来搜索。

ZGC收集器:

Oracle开发的收集器,书上这部份的内容也是特别多,我会抽重要的来讲(从一个学生的角度)。

1、与前面的收集器一样,ZGC也采用Region布局,其他一些资料称其为"Page"或"ZPage",ZGC的Region具有动态性,可以动态地创建和销毁,以及动态的区域容量大小,在x64硬件平台下,主要分为小、中、大三种:

(1)小型Region(Small Region):容量固定为2MB,用于放置小于256KB的小对象。

(2)中型Region(Medium Region):容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。

(3)大型Region(Large Region):容量不固定,可以动态变化,但必须为2MB的整数倍,用于放置4MB或以上的大对象。每个大型Region中只会存放一个大对象,这也预示着虽然名字叫作“大型Region”,但它的实际容量完全有可能小于中型Region,最小容量可低至4MB。大型Region在ZGC的实现中是不会被重分配;

2、ZGC垃圾收集器有个标志性的特点:染色指针技术。从前,需要在对象上存放一些个别的信息,通常会在对象头中添加额外的字段,但是在对象移动的过程中,怎么确保这些数据的可访问性问题呢?ZGC的染色指针直接把标记信息存放在引用对象的指针上,至于为什么指针上能存储额外的信息,这些就跟硬件有关里,这里就不展开讲了,感兴趣地可以去看书;

3、ZGC的执行过程分为四步:

(1)并发标记,这里大体和前面所讲的"标记三兄弟"类似,区别是ZGC会把标记存放在染色指针中的Marked 0、Marked 1标志为;

(2)并发预备重分配,这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,将这些Region组成重分配集(Relocation Set);

(3)并发重分配,重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。上面提到的大对象将不会被重分配;

(4)并发重映射,修正整个堆中指向重分配集中旧对象的所有引用,这里从目标角度来看和S收集器的并发引用更新是一样的,修正之后,原来记录新旧对象关系的转发表就可以释放掉了。


星期天的内容讲完了,现在回溯到星期六下午,主要做了Redis的消息队列实现,学习了三个Redis的消息队列类型,最后采用了Stream消息队列来完成秒杀模块的功能。其实用下来还是感觉RabbitMQ更加好用方便,毕竟Redis的消息队列会存在大大小小的缺陷,这里说下Stream消息队列中消费者组,消费者组是一组具有相同消费者组名称的消费者的集合。每个消费者组都有一个唯一的名称,并且可以有多个消费者加入该组。消费者组允许多个消费者共同处理消息,并提供了负载均衡和消息分配的机制,同时还能自动跟踪消费者的状态。这使得消费者组在处理高并发、大量消息的场景下非常有用。另外,Stream消息队列是Redis5.0中引入的,GItHub上微软维护的Windows版本的Redis已经2018年宣布停止维护了,最后的版本是3.2,所以为了更新到5.0费了我不少功夫。下面直接放总结。

收工。

相关推荐

  1. 标记算法

    2023-12-06 06:00:04       22 阅读
  2. 关于标记算法

    2023-12-06 06:00:04       27 阅读
  3. 标记法详解

    2023-12-06 06:00:04       14 阅读

最近更新

  1. TCP协议是安全的吗?

    2023-12-06 06:00:04       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2023-12-06 06:00:04       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2023-12-06 06:00:04       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2023-12-06 06:00:04       18 阅读

热门阅读

  1. day70

    day70

    2023-12-06 06:00:04      32 阅读
  2. Qt与Sqlite3

    2023-12-06 06:00:04       41 阅读
  3. gitlab 迁移-安装-还原

    2023-12-06 06:00:04       27 阅读
  4. gitlab 迁移-安装-还原

    2023-12-06 06:00:04       33 阅读
  5. 驱动模块--内核模块

    2023-12-06 06:00:04       42 阅读
  6. UI界面程序鼠标右键弹出菜单的一些事

    2023-12-06 06:00:04       39 阅读
  7. 学习TypeScrip3(接口和对象类型)

    2023-12-06 06:00:04       38 阅读
  8. vue自定义指令配置小程序按钮权限

    2023-12-06 06:00:04       36 阅读