内存分配概要

前段时间在园子里看到有人提到了GC学习的重要性,很赞同他的观点。充分了解GC可以帮助我们更好的认识.NET的设计以及为何在云原生开发中.NET Core会占有更大的优势,这也是一个程序员成长到更高层次所需要经历的过程。在认识GC的过程中,我们先看一下.NET中内存分配的概要知识。
.NET分配内存,主要依据托管资源和非托管资源进行分配。托管资源分配到了托管堆中并受CLR的管理,非托管资源分配到了非托管堆中。该节主要讨论托管资源的分配。
CLR支持两种基本类型:值类型和引用类型。CLR对这两种类型在运行时有两种分配方式:

内存的分配过程如下图所示

需要注意的是,CLR还要维护一个指针,称为NextObjPtr,这个指针指向下一个对象再堆中的分配位置。初始化时,NextObjPtr设为地址空间区域的基地址。一个区域被非垃圾对象填满后,CLR会分配更多的区域,指针也会不断偏移。new操作符会返回对象的引用,就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆时的地址。

垃圾回收算法与GC运行机制

常用的垃圾回收算法主要有引用计数算法和引用跟踪算法。引用计算有着明显的缺陷,.NET使用的垃圾回收算法是引用跟踪法。小记:关于垃圾回收算法,我记得有一个知识点,在C#中如果出现了循环引用是否会导致内存溢出?如果比较了解这两种算法就会知道不会溢出。

GC Root

引用跟踪算法,通过一系列GCRoot对象作为起始点,从这些点开始向下搜索,搜索的路径成为引用链,当一个对象到GC没有任何引用链,说明对象可以被回收。

GC Root可以类比树来解释

GC根节点存在于堆栈中,指向Teacher引用对象。它包含一个ArrayList订单集合,由Teacher对象引用。集合本身也包含对其元素的引用,随着搜索深度的增加,树也不断长大。

GC根节点的引用源来自

(1)、堆栈
(2)、全局或静态变量
(3)、CPU寄存器
(4)、互操作引用(COM / API调用中使用的.NET对象)
(5)、对象终结引用(objects finalization references)

GC运行机制

GC引入了代的概念,分为三种代,G0、G1、G2,G0对象生存周期较短,越往后生存周期越长(虽然G2中由于直接存储了大对象,又由于G2不是每次都会扫描,所以大多数情况下,G2中的对象的生存周期比G0中的更长)。

GC运行如下图所示

需要注意的是,CLR想要进行垃圾回收时,会立即挂起执行托管代码中的所有线程,正在执行非托管代码的线程不会挂起。所以再多线程环境下,可能会出现莫名其妙的诡异问题。

下图为GC的整体运行流程,包含五个步骤:

垃圾回收时机与模式

CLR会在一下情况发生时,执行GC操作

1、当GC的代的预算大小已经达到阈值而无法对新对象分配空间的时候,比如GC的第0代已满;

  2、显式调用System.GC.Collect()(显示调用要慎重,因为手动调用可能会与自动执行的GC冲突,从而导致无法预知的问题);

  3、其他特殊情况,比如,操作系统内存不足、CLR卸载AppDomain、CLR关闭,甚至某些极端情况下系统参数设置改变也可能导致GC回收。

关于GC模式主要有

  • WorkStation GC
  • Server GC
  • Concurrent GC
  • Non-Concurrent GC
  • Background GC

详细信息请参阅:https://www.cnblogs.com/dacc123/p/10980718.html,这篇文章关于GC模式的说明比较详细。

.NET Core 3.0中的GC优化处理

.NET Core 3.0默认更好的支持Docker资源限制,官方团队也在努力让.NET Core成为真正的容器运行时,使其在低内存环境中具有容器感知功能并高效运行。

GC堆限制

.NET Core减少了CoreCLR默认使用的内存,如G0代内存分配预算,以更好地与现代处理器缓存大小和缓存层次结构保持一致。

在新的创建的GC堆数量的策略里,GC保留了一个内存片段,每个堆最小是16M,在低内存限制的机器上也可以很好的运行。在多核CPU的机器上运行时,系统并没有设置CPU的核数限制。例如,如果在48核计算机上设置160 MB内存限制,则不需要创建48个GC堆。也就是说如果设置160 MB限制,则只会创建10个GC堆。如果未设置CPU限制,应用程序可以利用计算机上的所有核心。

有了这样的新策略,可以不需要启用Docker环境下的.NET Core应用的工作站GC的工作负载。

支持Docker内存限制

Docker资源限制建立在cgroup之上,而cgroup是Linux的内核功能。从运行时的角度来看,我们需要定位cgroup原语。

设置cgroup限制时的.NET Core 3.0内存使用规则:

  • 默认GC堆大小:容器上cgroup内存限制的最大值20MB或最大值的75%
  • 每个GC堆的最小保留段大小16MB,这将减少在具有大量内核和小内存限制的计算机上创建的堆数

为了支持容器方案,添加了2个HardLimit配置:

  • GCHeapHardLimit - 指定GC堆的硬限制
  • GCHeapHardLimitPercent - 指定允许此进程使用的物理内存的百分比

如果同时指定了两者,则首先检查GCHeapHardLimit,并且只有在未指定GCHeapHardLimit时才检查GCHeapHardLimitPercent。

如果两者都未指定,但进程正在有内存限制的容器中运行,则默认是使用如下设置:

max(20mb,容器内存限制的75%)

如果指定了hardlimit配置,并且程序在有内存限制的容器中使用,GC堆的使用不会超过hardlimit限制,但总内存仍然受容器的内存限制。所以当我们统计内存消耗时,基于容器内存限制得出的数据。

举例:

进程在设置了200MB限制的容器中运行,用户还将GCHeapHardLimit配置为100MB。

如果把GC限制中100MB限制中的50MB用于GC,而容器限制中剩余的100MB用于其他用途,那么内存消耗即为(50+100)/200=75%。

GC将更积极地执行资源回收与释放,因为GC堆越接近GCHeapHardLimit限制,就越能实现提供更多可用内存的目标,也越能使得应用程序可以继续而又安全地运行。如果算法计算出的结果认为此时的GC效率低下,那么将避免持续执行完全阻塞的GC。

即使GC堆完全压缩,GC依然会抛出一个OutOfMemoryException异常出来,这是因为所分配的堆大小超过了GCHeapHardLimit的限制。

由此可见,.NET Core 3.0的设计是要稳定运行于有资源限制的容器中。

支持DockerCPU限制

在CPU限制的情况下,Docker上设置的值将向上舍入为下一个整数值。此值是CoreCLR使用的最大有效CPU核数。

默认情况下,ASP.NET Core应用程序启用了服务器GC(它不适用于控制台应用程序),因为它可以实现高吞吐量并减少跨核心的争用。当进程仅限于单个处理器时,运行时会自动切换到工作站GC。即使您明确指定使用服务器GC,工作站GC也将始终用于单核环境。

通过计算CPU繁忙时间,设置CPU限制,我们避免了线程池的各种推导性竞争:

  • 尝试分配更多的线程以增加CPU繁忙时间
  • 尝试分配更少的线程,因为添加更多的线程不会提高吞吐量

参考资料:

https://devblogs.microsoft.com/dotnet/using-net-and-docker-together-dockercon-2019-update/

https://github.com/dotnet/designs/blob/master/accepted/support-for-memory-limits.md

https://www.cnblogs.com/dacc123/p/10980718.html

https://blog.csdn.net/koudaidai/article/details/7794793

从CLR GC到CoreCLR GC看.NET Core对云原生的支持的更多相关文章

  1. 【C# GC】C# GC何时被调用

    首先要指导GC的机制,这部分转载自:http://blog.csdn.net/qq_20949153/article/details/52188882CLR:公共运行时环境,管理托管堆.CTR:设置引 ...

  2. Minor GC、Major GC和Full GC之间的区别(转)

    在 Plumbr 从事 GC 暂停检测相关功能的工作时,我被迫用自己的方式,通过大量文章.书籍和演讲来介绍我所做的工作.在整个过程中,经常对 Minor.Major.和 Full GC 事件的使用感到 ...

  3. Minor GC、Major GC和Full GC之间的区别

    在 Plumbr 从事 GC 暂停检测相关功能的工作时,我被迫用自己的方式,通过大量文章.书籍和演讲来介绍我所做的工作.在整个过程中,经常对 Minor.Major.和 Full GC 事件的使用感到 ...

  4. gc实例与gc报告的阅读

    gc报告的阅读 首先我们看一条gc报告 D:\杂项\java>java -verbose:gc -Xms20m -Xmx20m -Xmn10m -XX:+PrintGCDetails -XX:+ ...

  5. GC参考手册 —— GC 调优(基础篇)

    GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理.初学者可能会被 200 多个 GC参数弄得一头雾水, 然后随便调整几个来试试结果,又或者修改几行代码来测试.其 ...

  6. GC参考手册 —— GC 调优(工具篇)

    JVM 在程序执行的过程中, 提供了GC行为的原生数据.那么, 我们就可以利用这些原生数据来生成各种报告.原生数据(raw data) 包括: 各个内存池的当前使用情况, 各个内存池的总容量, 每次G ...

  7. GC参考手册 —— GC 算法(实现篇)

    学习了GC算法的相关概念之后, 我们将介绍在JVM中这些算法的具体实现.首先要记住的是, 大多数JVM都需要使用两种不同的GC算法 —— 一种用来清理年轻代, 另一种用来清理老年代. 我们可以选择JV ...

  8. JVM垃圾回收(二)- Minor GC vs Major GC vs Full GC

    Minor GC vs Major GC vs Full GC 垃圾回收的活动会清理对内存中的不同区域,这些事件一般被称为Minor,Major以及Full GC events.本章我们会讨论这些清理 ...

  9. gc之六--Minor GC、Major GC、Full GC以及Mixed GC之间的区别

    目录: GC之一--GC 的算法分析.垃圾收集器.内存分配策略介绍 GC之二--GC日志分析(jdk1.8)整理中 GC之三--GC 触发Full GC执行的情况及应对策略 gc之四--Minor G ...

随机推荐

  1. iOS8扩展插件开发配置

    一.iOS8扩展插件概述 WWDC14除了发布了OS X v10.10和switf外,iOS8.0也开始变得更加开放了.说到开放,当然要数应用扩展(App Extension)了.顾名思义,应用扩展允 ...

  2. iview modal 弹框 模板

    iview modal 弹框 模板 <!-- * @description 上传图片 * @fileName camera.vue * @author 彭成刚 * @date // :: * @ ...

  3. 字符串KMP || POJ 2185 Milking Grid

    求一个最小矩阵,经过复制能够覆盖原矩阵(覆盖,不是填充,复制之后可以有多的) *解法:横着竖着kmp,求最大公倍数的做法是不对的,见http://blog.sina.com.cn/s/blog_69c ...

  4. idea集成 MyBatis Generator 插件,自动生成dao,model,sql map文件

    1.集成到开发环境中 以maven管理的功能来举例,只需要将插件添加到pom.xml文件中即可.(注意此处是以plugin的方式,放在<plugins></plugins>中间 ...

  5. 《发际线总是和我作队》第八次团队作业:Alpha冲刺 第五天

    项目 内容 这个作业属于哪个课程 软件工程 这个作业的要求在哪里 实验十二 团队作业8:软件测试与Alpha冲刺实验十一 团队作业7:团队项目设计完善&编码 团队名称 发际线总和我作队 作业学 ...

  6. Map与对象关系的思考之P1563玩具谜题

    P1563 玩具谜题 结论: map在一些情况有种"对象"的意味,在JSON中,对象可以用K-V格式存储:mybatis中参数是map或者对象都可以实现解析...k-v格式的数据存 ...

  7. IOI2008 Island 岛屿

    题目描述: bz luogu 题解: 裸的基环树直径. 代码: #include<queue> #include<cstdio> #include<cstring> ...

  8. 1. 调整InnoDB系统表空间的大小

    1. 调整InnoDB系统表空间的大小 介绍如何增大或减小InnoDB系统表空间的大小 . 1.1 增加InnoDB系统表空间大小 增加InnoDB系统空间最简单的方法就是,在配置文件中配置autoe ...

  9. MySQL之federated

    由于夸服务器查询的限制,federated能够使得所有的表像是在同一台服务器上查询 (show engines-->no-->在my.ini里面添加fedrated) 经过测试,在开启fe ...

  10. angular2 启动步骤

    以下内容转自网络 1. 创建项目文件夹 创建一个新的文件夹来保存你的项目,比如一开始有个self就好了 2.安装基础库 首先确保已经安装了node.js 我们使用 npm package manage ...