.Net Core中GC调优

简介

  GC管理你服务的内存分配和释放,GC是运行公共语言运行时(CLR Common Language Runtime)中,GC可以帮助开发人员有效的分配内存和和释放内存,大多数情况下是不需要去担心的,但是有时候服务总是是出现莫名的问题,所以还是有必要了解一下GC的基础知识的。

  1、GC调优本质是什么?

    GC调优本质是针对堆对象的回收及优化,把控程序运行状态,在有限的硬件资源中稳定运行下去,不让它造成内存溢出。

  2、GC调优指标是什么?

    暂停时间是GC很重要的一个指标,意思是在GC暂停多长时间才能执行其它工作(常说的"卡顿" 或 "挂起线程"),暂停时间较长就会直接影响程序工作延迟。。。(如果是大型项目至于会造成什么影响,脑补下。。。)

  注:如果线程当前正在运行时执行C / C ++代码,则GC可能需要等到该调用完成后才能挂起线程。因此,我们尽量使用托管代码而不是本机代码,GC暂停时间就越好。

CLR

  .NET程序是运行在 CLR : Common Language Runtime 之上。CLR 就像 JAVA 中的 JVM 虚拟机。CLR 包括了JIT编译器,GC(垃圾回收器),CIL(公共中间语言) CLI(公共语言基础架构)。

  概念:

    1、我们的程序都是在操作虚拟内存地址,从来不直接操作内存地址,即使是 Native Code。(注:虚拟内存在win中叫"虚拟内存" linux中叫"交换空间")

    2、一个进程会被分配一个独立的虚拟内存空间,我们定义的和管理的对象都在这些空间之中。

    3、虚拟内存空间中的内存 有三种状态:空闲 (可以随时分配对象),预定 (被某个进程预定,尚且不能分配对象),提交(从物理内存中分配了地址到该虚拟内存,这个时候才可以分配对象)

    4、CLR初始化GC 后,GC 就在上面说的虚拟内存空间中分配内存,用来让它管理和分配对象,被分配的内存叫做 Managed Heap 管理堆,每个进程都有一个管理堆内存,进程中的线程共享一个管理堆内存

    5、CLR中还有一块堆内存叫做LOH Large Object Heap 。它也是隶属于 GC 管理,但是它很特别,只分配大于 85000byte 的对象,所以叫做大对象,为什么要这么做呢?很显然大对象太难管理了,GC 回收大对象将很耗时,所以没办法,只有给这些 “大象” 另选一出房子,GC 这个“管理员” 很少管 “大象”。

  那么什么时候对象会被分配到堆内存中呢?

    所有引用类型的对象,以及作为类属性的值类型对象,都会分配在堆中。大于 85000byte 的对象扔到 “大象房” 里。

    堆内存中的对象越少,GC 干的事情越少,你的程序就越快,因为 GC 在干事的时候,程序中的其他线程都必须毕恭毕敬的站着不动(挂起),等 GC 说:我已经清理好了。然后大家才开始继续忙碌。所以 GC 一直都是在干帮线程擦屁股的事情。

    所以没有 GC 的编程语言更快,但是也更容易产生废物。

GC Generation

  那么 GC 在收拾垃圾的过程中到底做了什么呢?首先要了解 CLR 的 GC 有一个Generation **代 ** 的概念 GC 通过将对象分为三代,优化对象管理。GC 中的代分为三代:

    1、Generation0  零代或者叫做初代,初代中都是一些短命的对象,shorter object,它们通常会被很快清除。当 new 一个新对象的时候,该对象都会分配在 Generation 0 中。只有一段连续的内存

    2、Generation1  一代,一代中的对象也是短命对象,它相当于 shorter object 和 longer object 之间的缓冲区。只有一段连续的内存

    3、Generation2  二代,二代中的对象都是长寿对象,他们都是从零代和一代中选拔而来,一旦进入二代,那就意味着你很安全。之前说的 LOH 就属于二代,static 定义的对象也是直接分配在二代中。包含多段连续的内存

GC 回收类型

  GC 有两种形式:WorkStation GC和 Server GC 默认的.NET程序都是WorkStation GC

  WorkStation GC 和 Server GC区别:    

    1、Server GC 的 Generation 内存更大,64位操作系统 Generation 0 的大小居然有4G ,这意味着啥?在不调用GC.Collect 的情况下,4G 塞满GC 才会去回收。那样性能可是有很大的提升。但是一旦回收了,4GB 的“垃圾” 也够GC喝一壶的了。

    2、Server GC 拥有专门用来处理 GC的线程,而WorkStation GC 的处理线程就是你的应用程序线程。WorkStation 形式下,GC 开始,所有应用程序线程挂起,GC选择最后一个应用程序线程用来跑GC,直到GC 完成。所有线程恢复。而ServerGC 形式下: 有几核CPU ,那么就有几个专有的线程来处理 GC。每个线程都一个堆进行GC ,不同的堆的对象可以相互引用。所以在GC 的过程中,Server GC 比 WorkStation GC 更快。有专有线程,但并不代表可以并行GC哦。

  上面两个区别,决定了 Server GC 用于对付高吞吐量的程序,而WorkStation GC 用于一般的客户端程序足以。

  零代和一代 占用的内存因为他们都是短暂对象,所以叫做短暂内存块。 那么他们占用的内存大小是多大?32位和63位的系统是不一样的,不同的GC类型也是不一样的。

  .Net Core中GC调优

    注:ServerGC开启后是根据服务器CPU核心数来创建多个GC 而不是CPU物理数

GC回收模式

  GC 有三种模式:

    1、Non-Concurrent 非并行回收模式:在非并行模式下,回收时候会挂起所有其他的线程影响服务的性能。

    2、Concurrent GC 并行回收模式:并行会后可以解决非并行回收引起的线程挂起,让其他线程和回收线程一起运行,使服务可以更快的响应,并行回收只会发生在Generation 2中,Generation 0/1始终都是非并发的,因为他们都是小对象回收的速度很快。在并行回收的时候依旧可以分配对象到Generation 0/1中。

    3、Background GC 后台回收模式:Background GC 是 Concurrent GC的增强版本。

      Background GC 和 Concurrent GC 的区别:

        首先:Background GC 和 Concurrent GC 都是为了减少 因为 GC 而挂起工作线程的时间,从而提升用户交互体验,程序响应速度。

        其次:Background GC 和 Concurrent GC 一样,都是使用一个专有的GC 线程,并且都是在 Generation 2 中起作用。

        最后:Background GC 是 Concurrent GC 的增强版,在.NET 4.0 之前都是默认使用 Concurrent GC 而 .NET 4.0+ 之后使用Background GC 代替了 Concurrent GC。

      Background GC 比 Concurrent GC 多了什么:

        上面说到 Concurrent GC 在 Generation 2 中进行清理时,工作线程仍然可以在 Generation 0/1 中进行分配对象,但是这是有限制的,当 Generation 0/1 中的内存片段 Segment 用完的时候,就不能再分配了,直到 Concurrent GC 完成。

        而 Background GC 没有这个限制,因为 Background GC 在 Generation 2 中进行清理时,允许 Generation 0/1 进行清理,也就说是当 Generation 0/1 的 Segment 用完的时候,GC 可以去清理它们,这个GC 称作 Foreground GC ( 前台GC ) ,Foreground GC 清理完之后,工作线程就可以继续分配对象了。

        Background GC回收Generation 2的时允许了Generation 0/1 进行清理。

        在WorkStation GC下会使用一个专用的后台垃圾回收线程,而Server GC下会使用多个线程来进行回收。

        且Server GC下回收线程不会超时。

      所以 Background GC 比 Concurrent GC 减少了更多 工作线程暂停的时间。

总结:

  自从.NET Core 3.0开始对根据自己具体的应用场景去配置GC ,让GC 发挥最好的作用。比如启用Server GC 对于高吞吐量的程序有帮助,比如禁用 Concurrent GC 实际上对一个高密度计算的程序是有性能提升的。

  .NET 5 改动更大,而且.NET 5整体性能比.net core 3.1高20%,并且在GC这块.NET 5开放了更多配置,所以.NET 5很值得关注。

  参考:   

    1、.NET 5中的性能改进

      https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-5/

    2、垃圾收集的基本原理    

      https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals    

    3、垃圾收集的运行时配置选项 (注:这里面就有.net 版本对比与.NET5新开放的配置)

      https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector

    4、使用并发可视化工具了解不同的GC模式

      https://devblogs.microsoft.com/premier-developer/understanding-different-gc-modes-with-concurrency-visualizer/

    5、小型容器使用Server GC

      https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-0/

      https://devblogs.microsoft.com/dotnet/running-with-server-gc-in-a-small-container-scenario-part-1-hard-limit-for-the-gc-heap/

    6、.NET3新功能

      https://devblogs.microsoft.com/dotnet/announcing-net-core-3-preview-3/

上一篇:用c++做的通讯录管理系统


下一篇:PAT A1094 The Largest Generation (25分)