简介

在mark_phase阶段之后,所有对象都被标记为有用/垃圾对象。此时,垃圾回收器已经拥有启动垃圾回收的所有前置准备工作。

这个时候,垃圾回收期应该执行"清除回收"还是"压缩回收"呢?只有做一下试验才能得出理论支撑。

模拟压缩

这里会有一个悖论,如果你要知道压缩是否划得来,那你就得先压缩后查看其结果,才知道压缩的成本。

CLR团队如何解决这个问题呢?plan_phase阶段会计算与压缩过程相关的所有信息,而这些信息是以旁敲侧击的方式计算,并没有实际移动对象。这样我们就能从侧面知道压缩的结果

插头(plug)/间隙(gap)

模拟压缩阶段,CLR会将托管堆上的对象分为有空(plug)和没用(gap)两块,也就是所谓的插头和间隙.

通过将托管堆拆分成plug与gap,我们可以轻松计算出其重要信息

  1. 每个gap的大小和位置都会被记住,如果最终是清除回收,那么大多数gap都将成为Free的可用空间。
  2. 每个plug的位置与偏移量都会被记住,如果最终选择了压缩回收,则会使用重定位偏移量来移动plug

重排plug

计划阶段在重排 Plug 区块时,内部使用了一个单独分配器,所以此时plug是并没有被移动的.分配器仅将对象指针进行操作,进行模拟。

  1. 当遇到第一个plug时,分配器会找到根据对象自身的alloc_ptr指针,移动分配器的指针,并记录两个指针之间的偏移量,记为重定位偏移量
  2. 遇到下一个plug时,分配器会在上一个分配的基础上,继续分配。直到遇到最后一个plug。

这样,所有的重定位偏移量都被计算出来,因此GC可以准确的知道以下信息

  1. 压缩效率是多少?
  2. 如果是清除压缩,在哪里创建Free列表?
  3. 如果是压缩回收,plug如何移动?

plug数据结构

既然要模拟压缩,那么就意味着有数据结构来承载额外的信息。在 coreclr 源码中有一个叫 gap_reloc_pair 结构体记录Plug的信息。

gap没有专用的数据结构来存,大家可以思考一下。为什么?

  1. gap

    记录着plug前面gap大小
  2. reloc

    plug 新地址的相对旧地址的偏移量
  3. m_pair

    记录plug左右plug的位置(二叉排序树)



gap_reloc_pair的存储

按照常规方案,gap_reloc_pair的存储会在托管堆上单独开辟一段内存区间来存放,但是CLR团队非常巧妙的把gap_reloc_pair放在gap块中,因为gap不再使用,覆盖它是安全的,非常巧妙的设计!

将plug的信息存储在plug之前,这就是为什么即使是一个空对象也必须是24字节的原因

有人可能会问了,内存段的段首,第一个plug前面没有gap怎么办?

实际上,每一代的对象,都是从一个空对象开始的,因此即使是第一个插头,它也是有gap的。

眼见为实:plug前面的gap中存放着gap_reloc_pair

在bp coreclr!WKS::gc_heap::decide_on_compacting 下断点。

眼见为实:第一个plug,有一个天然的gap

代降级

因为pinned对象的存在,导致对象代的提升不是100%的,有可能会不升反降。在执行压缩的场景下,如果pinned对象出现在了一些特别尴尬的位置,GC会考虑给某些pinned对象降代或者不升代

举个例子,如果不存在降代现在,GC堆会发生什么情况

可以看到,0代段被压缩到很小,导致没分配几次内存又要GC,又会导致STW非常频繁,从而使得程序卡顿,CPU增高,吞吐量降低等现在

这个时候,只有选择不升代,或者降代,才能维持好GC代之间的平衡。

降级是一种优化,确保更多的内存碎片被重用。

眼见为实

未GC前,pinned为0代,正常情况下,它会升为1代。

因为该pinned对象非常尴尬的出现在了一大批gap对象之后,如果升代,会导致前面这一片gap对象空间同样被纳入1代的代边界范围,这极大的缩小了0代的代边界。

因此,ClR选择将Pinned对象不升代

番外篇:无效结构的内存转储

有时候,我们在plan_phase阶段,通过!dumpheap 指令查看托管堆的时候,会发现托管堆看不了。提示如下信息

还记得之前说过的gap_reloc_pair数据结构吗?它被CLR团队非常巧妙的放在了gap中。

问题就在于此,如果你的dump正好在plan_phase执行过程中,因为gap上的原始内容被gap_reloc_pair覆盖,所以此时的托管堆相当于是被破坏状态。因此CLR为了防止你被脏数据误解,直接不让你观察。

决定压缩的诱因

在模拟压缩阶段,GC根据会计算出压碎率,碎片大小的,并辅助其它条件。来决定是否执行压缩回收。

其它条件可能是,主动触发,也可能是OOM之前的最后一次Full GC ,或者是临时段空间不足

其核心方法为

BOOL gc_heap::decide_on_compacting (int condemned_gen_number,
size_t fragmentation,
BOOL& should_expand)

通过返回Bool,来告诉下一阶段应该执行清除回收还是压缩回收

眼见为实

https://github.com/dotnet/runtime/blob/main/src/coreclr/gc/gc.cpp

.NET Core GC计划阶段(plan_phase)底层原理浅谈的更多相关文章

  1. Java线上问题排查神器Arthas快速上手与原理浅谈

    前言 当你兴冲冲地开始运行自己的Java项目时,你是否遇到过如下问题: 程序在稳定运行了,可是实现的功能点了没反应. 为了修复Bug而上线的新版本,上线后发现Bug依然在,却想不通哪里有问题? 想到可 ...

  2. CSRF漏洞原理浅谈

    CSRF漏洞原理浅谈 By : Mirror王宇阳 E-mail : mirrorwangyuyang@gmail.com 笔者并未深挖过CSRF,内容居多是参考<Web安全深度剖析>.& ...

  3. JAVA CAS原理浅谈

    java.util.concurrent包完全建立在CAS之上的,没有CAS就不会有此包.可见CAS的重要性. CAS CAS:Compare and Swap, 翻译成比较并交换. java.uti ...

  4. 如何把Java代码玩出花?JVM Sandbox入门教程与原理浅谈

    在日常业务代码开发中,我们经常接触到AOP,比如熟知的Spring AOP.我们用它来做业务切面,比如登录校验,日志记录,性能监控,全局过滤器等.但Spring AOP有一个局限性,并不是所有的类都托 ...

  5. CAS+SSO原理浅谈

    http://www.cnblogs.com/yonsin/archive/2009/08/29/1556423.htmlSSO 是一个非常大的主题,我对这个主题有着深深的感受,自从广州 UserGr ...

  6. Mysql锁原理浅谈

    锁类型/引擎 行锁 表锁 页锁 MyISAM 有 InnoDB 有 有 BDB(被InnoDB取代) 有 有 锁的分类 表锁:开销小,加锁快,不会死锁,粒度大,冲突率高,并发低. 行锁:开销大,加锁慢 ...

  7. php模板原理PHP模板引擎smarty模板原理浅谈

    mvc是开发中的一个伟大的思想,使得开发代码有了更加清晰的层次,让代码分为了三层各施其职.无论是对代码的编写以及后期的阅读和维护,都提供了很大的便利. 我们在php开发中,视图层view是不允许有ph ...

  8. PHP的模板引擎smarty原理浅谈

    mvc是开发中的一个伟大的思想,使得开发代码有了更加清晰的层次,让代码分为了三层各施其职.无论是对代码的编写以及后期的阅读和维护,都提供了很大的便利. 我们在php开发中,视图层view是不允许有ph ...

  9. Docker 基础底层架构浅谈

    docker学习过程中,免不了需要学习下docker的底层技术,今天我们来记录下docker的底层架构吧! 从上图我们可以看到,docker依赖于linux内核的三个基本技术:namespaces.C ...

  10. Java中的SPI原理浅谈

    在面向对象的程序设计中,模块之间交互采用接口编程,通常情况下调用方不需要知道被调用方的内部实现细节,因为一旦涉及到了具体实现,如果需要换一种实现就需要修改代码,这违反了程序设计的"开闭原则& ...

随机推荐

  1. 基于Java+SpringBoot+Mysql实现的古诗词平台功能设计与实现四

    一.前言介绍: 1.1 项目摘要 随着信息技术的迅猛发展和数字化时代的到来,传统文化与现代科技的融合已成为一种趋势.古诗词作为中华民族的文化瑰宝,具有深厚的历史底蕴和独特的艺术魅力.然而,在现代社会中 ...

  2. Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler)

    Quartz集群增强版_01.集群及缺火处理(ClusterMisfireHandler) 转载请著名出处 https://www.cnblogs.com/funnyzpc/p/18542452 主要 ...

  3. .NET Core 委托底层原理浅谈

    简介 .NET通过委托来提供回调函数机制,与C/C++不同的是,委托确保回调是类型安全,且允许多播委托.并支持调用静态/实例方法. 简单来说,C++的函数指针有如下功能限制,委托作为C#中的上位替代, ...

  4. 低功耗4G模组:RSA算法示例

    ​ 今天我们学习合宙低功耗4G模组Air780EP_LuatOS_rsa示例,文末[阅读原文]获取最新资料. 一.简介 RSA算法的安全性基于:将两个大质数相乘很容易,但是想要将其乘积分解成原始的质数 ...

  5. 哪些网站可以申请免费的纯IP地址https证书

    申请免费纯IP地址HTTPS证书,您可以按照以下步骤进行: 一.选择证书颁发机构(CA) 目前,虽然一些大型云服务提供商(如阿里云.华为云.腾讯云等)已经取消了免费一年期SSL证书的供应,但仍有一些C ...

  6. 开源 - Ideal库 - 枚举扩展设计思路及实现难点(三)

    今天想和大家分享关于枚举扩展设计思路和在实现过程中遇到的难点. 01.设计思路 设计思路说起来其实也很简单,就是通过枚举相关信息:枚举值.枚举名.枚举描述.枚举项.枚举类型,进行各种转换,通过一个信息 ...

  7. php 超过64位进制和10进制的转换

    有时候想把一个很大的数尽量用更少的空间存储起来,那么就可以采用很大的进制来存储它,比如说,一个大于等于10小于等于16数字使用10进制就需要两位,使用16进制就只需要1位,那就等于帮程序省了一位的空间 ...

  8. 能不能用uni开发一个线上运动会的APP、小程序?

    引言:uni-app凭借其强大的跨平台能力,成为开发AI运动类APP和小程序的首选框架.本文旨在探讨基于uni进行开发AI运动小程序.APP开发,以及开发过程中遇到的技术难点,并为您介绍一个开箱即用的 ...

  9. 如何快速构建基于Spring4.0的Rest API(攻略)

    本文由 ImportNew - 邬柏 翻译自 javacodegeeks.如需转载本文,请先参见文章末尾处的转载要求. ImportNew注:如果你也对Java技术翻译分享感兴趣,欢迎加入我们的 Ja ...

  10. Linux之JSON处理工具jq

    一个灵活的轻量级命令行JSON处理器 补充说明 jq 是 stedolan 开发的一个轻量级的和灵活的命令行JSON处理器,源码请参考 jq 项目主页 jq 用于处理JSON输入,将给定过滤器应用于其 ...