【JVM精通之路】垃圾回收-三色标记算法

2024-05-29 00:12

本文主要是介绍【JVM精通之路】垃圾回收-三色标记算法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先预期你已经基本了解垃圾回收的相关知识,包括新生代垃圾回收器,老年代垃圾回收器,以及他们的算法,可达性分析等等。


先想象一个场景

最开始黑色节点是GC-Roots的根节点,这些对象有这样的特点因此被选为垃圾回收的根节点。

虚拟机栈(栈桢中的本地变量表)中的引用的对象 
方法区中的类静态属性引用的对象 
方法区中的常量引用的对象 
本地方法栈中JNI的引用的对象

记不住没事~你只需要知道,这些对象都有长生不老的特点,它一直存在,所以从它开始遍历查找,才知道哪些对象没有人使用,可以被回收。

那么最开始堆里的对象默认都是白色的。

现在要开始垃圾回收了。

如果是你你怎么设计,嗯,我们因为有这张图从三维看二维开天眼立马知道哪些对象是活着的。

但是程序不知道,它需要遍历。这里需要注意一个其他博客都没有提到的点,这里是GC-Roots引用了右边的对象,而不是右边的对象引用了GC-Roots。

你可以这么思考,因为是Roots依赖了右边的某些对象,又因为Roots是持久存在的,那么右边某些对象也必须存在。所以是Roots引用了某些对象,所以这些对象必须存活。很多博客在这里没有说清楚引用关系。

如果是右边对象依赖了GC-Roots,那么右边的对象存在与否和Roots有什么关系?

因为人引用了空气,他需要空气,所以必须空气存在。

1.定义

黑色:一直存在不需要清理的对象,它用到的对象都确定需要存活。下一次不扫描黑色对象。

灰色:一直存在不需要清理的对象,它用到的其他对象还没确定是否存活。下一次从它开始扫描。

白色:扫描完毕后,还是白色的对象,就清理掉。

颜色的标记 记录在每个对象的头部信息的Markword中!

CMS有四步,初始标记(STW),并发标记,重复标记(STW),并发清除四步。

1.开始遍历

第一次遍历:

第二次遍历:

 第三次扫描:

 第四次扫描:

 第五次扫描:

 第六次扫描

 

 第七次扫描:

 然后这时候把所有白色对象清理掉。

以上的图的过程都是发生在并发标记中。先不考虑重复标记。

2.思考

2.1 三色标记解决了什么问题

思考一下,你现在幻身为一个线程,你只知道堆里有一堆对象,你并不知道他们是什么颜色,你现在只知道根节点在堆内存的哪个地址下。老板叫你去清理堆内存的废物对象。而且你随时可能会被CPU抽掉灵魂。(线程被阻塞)

你:我特么不能每次醒过来都从根节点去遍历吧。不行我得有个记事本,(线程可以访问标记记录集合)记住哪些我标记了的。这样下次直接从已经标记的对象开始遍历。

那这时候只需要黑白二色标记就够了。

但是有几个问题:

1.黑色太多了。我需要从很多黑色对象开始找,费时,麻烦!

2.我在把对象18由白色变黑的过程中,如果对象7对象18抛弃了。那我是不是多标了。让本来应该死亡清除的对象18没有被清除。

3.现在堆内存中全都黑了,我要清理垃圾了。正要清理的时候,对象7突然引用了对象17,这时候就会出现漏标的问题。

如何解决呢?

思考...


问题1,那么能不能有个中间态。把还需要往下扫描的对象变成灰色,把不需要继续往下扫描的对象变成黑色。这时候往回看上文的扫描图,可以看见灰色节点的集合是非常少的,因此每次我只需要从较少的节点开始扫描

好了我手里有两个笔记本了。一个灰色笔记本,一个黑色笔记本。

解决了扫描对象过多的问题的同时也能解决STW的时间。

重新想起CMS有四步过程,初始标记(STW),并发标记,重复标记(STW),并发清除四步。

因为重新标记阶段也只需要从灰色节点开始扫描了。因为黑色是确定是活的,就算重新标记之后死掉了,也最多变成浮动垃圾,因为重新标记阶段是STW的,所以也不会有引用的变化。

并且只有并发标记的过程才会有引用的变化。


问题2,本来应该清除的对象没有被清除,这问题不大,当成浮动垃圾下一次垃圾回收再扫描一次就行了。多标本质造成浮动垃圾的问题。问题不大。

问题3,漏标怎么办,不能错杀啊。没有被标记的对象是要被杀掉的。不能被错杀。那么我必须stop the world了!STW。

只有停止所有用户线程才能避免我在标记对象的过程中,有对象复活了但是没被我标记,被活埋了!这个很重要。并不是因为三色标记才能避免漏标问题。三色标记只是为了减少STW的时间,以及减少遍历树的时间。

很多博客这里压根没讲清楚因果关系。只告诉你三色标记和标记清除算法里,有初始标记,并发标记,重复标记,怎么变色,多标,漏标问题。但是没把问题和解决方案对应起来。

我们都知道CMS有四步,初始标记(STW),并发标记,重复标记(STW),并发清除四步。

我不能从GC-Roots开始遍历的时候就STW一直到并发清除吧。STW这么长那么CMS的意义就不存在了。那么考虑哪些步骤可以与用户线程并发执行。

初始标记:将直接与GC-Roots的对象变灰,这部分速度很快,且不能出错,可以STW,性价比高。

并发标记:需要将对所有灰色节点遍历树结构,比较耗时,这部分肯定不能STW。

重复标记:这部分是最后扫尾工作,肯定不能让用户线程来参一脚,必须STW。并且修复并发标记时的错误标记,把并发标记漏标的白色变灰。

这里很多博客也没说清楚是什么错误。那么我们研究一下:并发标记只会让白变灰,或让灰变黑。

因为到重复标记时,堆内存只有黑白色了。

那么修复错误:

让白变灰:因为并发标记没有遍历到的对象一直白色,并发标记到重复标记开始之前如果有黑色对象引用到了白色对象。那么重复标记需要将白变灰。同时它是STW,这时变灰正好解决漏标。

为什么没有让黑变灰:正常来说并发标记标记为黑色后,但是之后黑色对象如果没有被任何对象引用了。需要将它变灰。但是重复标记只是扫尾工作,它是STW的,并且需要解决漏标问题。如果此时又让黑变灰,那么是否又考虑让灰变白。但是这两步完全可以放到下一轮垃圾回收的并发标记中去做,因为并发标记不STW,同时也能扫描到灰色对象。这里也是CMS单次回收不能很好解决浮动垃圾的问题,他可以解决,但是没必要。

之后并发清除可以清除白色对象了。

总结:

也就是三色标记法,可以区分标记的过程中的粒度,从而让垃圾回收器根据不同的标记阶段,更好的做出不一样的行为,并且对不同的行为采用不一样的STW规则,从而减少STW时间,增加用户线程运行时间。黑色就是需要活着的对象,灰色就是还需要往下遍历的对象,白色就是需要清除的对象。三个颜色也是标明了对象处于的扫描阶段。

这篇关于【JVM精通之路】垃圾回收-三色标记算法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第