关于.NET垃圾回收(GC)的基本问题

如何获取GC(垃圾回收器)的STW(暂停)时间? 在现代的容器化和微服务应用中,因为分布式的环境和错综复杂的调用关系,APM(Application Performance Monitoring 应用性能监控)显得尤为重要,它通过采集应用程序各种指标和请求链路,让你知道系统当前的状态和值得优化的点,另外能帮助你发现应用程序的异常,帮助你更方便的定位问题。对于.NET这样带GC(Garbage Collector 垃圾回收器)的平台来说,GC的指标也尤为重要,采集可以帮助我们分析内存泄漏、优化系统性能等等。在公司内部已经可以采集比较全面的.NET GC指标, 阅读详情

在.Net应用程序中很多问题都是没有正确的理解垃圾回收的工作原理而导致的,这里有一些关于GC的基本问题,如果看到问题答案心里都有数,那就请略过这篇,不然就一起来复习下吧。

  • 什么是代?
  • 什么时候发生垃圾回收?
  • 什么是大对象堆?
  • 什么是root?
  • 什么是finalizer?
  • 什么是finalizequeue?
  • 什么情况下会发生out ofmemory exception?
  • 什么情况下要实现IDisposible接口?
  • 什么情况下用GC.Collect?
  • GC有哪几种模式?

 

什么是代?

垃圾回收器使用了代的机制目的是提升性能,总共分为三代(Gen0, Gen1, Gen2)。GC的工作机制基于以下假设, 

  1. 对象越新,生存期越短
  2. 对象越老,生存期越长
  3. 回收堆的一部分比回收整个堆时间短

 

在应用程序的生命周期中,最近新建的对象被分配在第0代,在一次垃圾回收之后存活下来的进入下一代。这样可以使GC专注于回收最有可能存在更多可回收对象的第0代(最近分配的最有可能很快被释放)。

 

什么时候发生垃圾回收?

  1. 第0代满
  2. 代码显示调用GC.Collect方法
  3. Windows报告内存不足 - CLR注册了 Win32     CreateMemoryResourceNotification和QueryMemoryResourceNotification监视系统总体内存使用情况,如果收到Window报告内存不足的通知,强行执行GC。
  4. CLR卸载AppDomain
  5. CLR关闭

 

什么是大对象堆?

采用大对象堆是垃圾回收另外一个性能提升的策略,任何大于等于85000byte的对象都被视为大对象在特殊的大对象堆中分配。

 

大对象堆的回收策略

  1. 大对象堆被认为是第2代一部分,大对象堆回收时候同时回收第2代
  2. 大对象堆不进行压缩操作-因为太耗时耗力

 

根据该策略我们可以推测如果大对象频繁的被分配将造成频繁的第2代垃圾回收(即完全垃圾回收),对性能造成较大影响。 

 

什么是root?

静态对象

方法参数

局部变量

CPU寄存器

 

什么是finalizer?

大多数时候我们创建的类不包含非托管资源,因此只需要直接使用,CLR自然会判断其生命周期结束而后回收相应的托管资源。但如果我们创建了含有非托管资源的类,CLR提供了finalize机制来帮助自动释放非托管资源。

实现finalizer的语法与析构函数类似,实现了这个类似于析构函数的方式实际上被隐式转换成了重载父类的Finalize方法(object类默认提供了finalize方法)。

class Car
{
    ~Car()  // destructor
    {
        // cleanup statements...
    }
}

转换后

protected override void Finalize()
{
    try
    {
        // Cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

什么是finalizequeue?

在新建一个类的实例时,如果该类定义了Finalize方法,那么该类在构造器调用之前会将指向该对象的指针存放在一个叫finalization list中。垃圾回收时如果该对象被认定为垃圾,那么CLR会从finalizationlist中查找是否存在相应的对象指针,如果存在则将该指针移除,然后在freachable队列中加入该对象指针,CLR提供了一个高优先级的Finalizer线程来专门负责调用feachable队列中对象的finalize方法以释放资源。

 

什么情况下会发生out of memory exception?

在一个内存分配请求到达时,CLR发现第0代没有足够空间从而触发第0代GC,如果还是没有足够的内存,CLR发起完全GC,接下来CLR尝试增大第0代大小,如果没有足够的地址空间来增大第0代大小或满足内存分配请求,就会抛出OutOfMemoryException。

 

因此发生OutOfMemoryException的两个可能性是

  1. 虚拟地址空间耗尽
  2. 物理内存耗尽

 

什么情况下要实现IDisposible接口?

IDisposible最重要的目的是释放非托管资源,垃圾回收可以自动回收托管资源,但是对于程序中使用的非托管资源却一无所知,例如数据库连接,对象句柄等。

 

msdn中给了正确的IDisposable接口的正确实现,这个实现中最容易被误解的是protected virtual void Dispose(bool disposing)方法中布尔参数disposing的作用是什么。

 

参数disposing的目的是在显式调用Dispose方法或隐式调用Finalizer的情况下区别对待托管资源。在两种情况下对于非托管资源的处理是一致的,直接释放,不应该将非托管资源的释放放在if(disposing)的处理中。

 

为什么要区别对待托管资源?在显式调用dispose方法的时候可以保证其内部引用了托管资源未被回收,所有可以直接调用其相应的释放方法。但是finalizer被调用dispose的方法时,由于GC无法保证托管资源的释放顺序,所以在dispose方法中不应该再去访问内部的托管资源,有可能内部的托管资源已经被释放掉了。

using System;
using System.ComponentModel;
// The following example demonstrates how to create 
// a resource class that implements the IDisposable interface 
// and the IDisposable.Dispose method. 
public class DisposeExample
{
    // A base class that implements IDisposable. 
    // By implementing IDisposable, you are announcing that 
    // instances of this type allocate scarce resources. 
    public class MyResource: IDisposable
    {
        // Pointer to an external unmanaged resource. 
        private IntPtr handle;
        // Other managed resource this class uses. 
        private Component component = new Component();
        // Track whether Dispose has been called. 
        private bool disposed = false;
// The class constructor. 
        public MyResource(IntPtr handle)
        {
            this.handle = handle;
        }
// Implement IDisposable. 
        // Do not make this method virtual. 
        // A derived class should not be able to override this method. 
        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method. 
            // Therefore, you should call GC.SupressFinalize to 
            // take this object off the finalization queue 
            // and prevent finalization code for this object 
            // from executing a second time.
            GC.SuppressFinalize(this);
        }
// Dispose(bool disposing) executes in two distinct scenarios. 
        // If disposing equals true, the method has been called directly 
        // or indirectly by a user's code. Managed and unmanaged resources 
        // can be disposed. 
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed. 
        protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called. 
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources. 
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }
// Call the appropriate methods to clean up 
                // unmanaged resources here. 
                // If disposing is false, 
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;
// Note disposing has been done.
                disposed = true;
}
        }
// Use interop to call the method necessary 
        // to clean up the unmanaged resource.
        [System.Runtime.InteropServices.DllImport("Kernel32")]
        private extern static Boolean CloseHandle(IntPtr handle);
// Use C# destructor syntax for finalization code. 
        // This destructor will run only if the Dispose method 
        // does not get called. 
        // It gives your base class the opportunity to finalize. 
        // Do not provide destructors in types derived from this class.
        ~MyResource()
        {
            // Do not re-create Dispose clean-up code here. 
            // Calling Dispose(false) is optimal in terms of 
            // readability and maintainability.
            Dispose(false);
        }
    }
    public static void Main()
    {
        // Insert code here to create 
        // and use the MyResource object.
    }
}

什么情况下用GC.Collect? 

大多数情况下我们都应该避免调用GC.Collect方法,让垃圾回收器自动执行,但是还是有些情况比如在某个时刻会发生一次非重复性事件导致大量的对象死亡,这个时候我们可以不依赖于垃圾回收器的自动机制,手动调用GC.Collect方法。 

记住不要为了改善应用程序相应时间而调用GC.Collect,而是应该处于减少工作集的目的。 

GC有哪几种模式? 

工作站模式(workstation mode) 

这种模式下GC假设机器上运行的其他应用程序对CPU资源要求不高,并且该模式可以使用并发或非并发的模式运行。 

并发模式是默认的工作站模式,该模式下垃圾回收器分配一个额外的后台线程在应用程序运行时并发回收对象。一个线程因为分配对象造成第0代超出预算时,垃圾回收器挂起所有线程,判断需要回收哪些代,如果需要回收第2代,就会增加第0代的大小。然后应用程序恢复执行。并发回收是为了给用户更好的交互体验,适合客户端应用程序,但是同时要注意并发回收对性能有损害,使用更多地内存。 

禁用并发模式的配置为 

<configuration>
    <runtime>
        <gcConcurrentenabled="false"/>
    </runtime>
</configuration>

服务器模式 (servermode)

这种模式下GC假设机器上没有运行其他应用程序,所有的CPU都可以用来进行垃圾回收操作。在这种情况下虚拟内存按照CPU数量划分区域分开对待,每个CPU上都运行一个GC线程负责回收自己的区域。 

参考文档

Memory Management andGarbage Collection in the .NET Framework

http://msdn.microsoft.com/en-us/library/hh156531.aspx


 

.NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分) 垃圾回收机制有一些未定义部分,一般来说不要依赖于这些未定义部分编程,否则容易出现一些诡异的 bug 或者不稳定的现象。 本文介绍局部变量这部分的细节,而这点在 .NET Framework 和 .NET Core 默认情况下的表现有差别。 本文内容问题代码原因 问题代码 看看下面这段代码,你觉得会输出 Foo is collected 吗? class Program { static void Main(string[] args) { new WeakReference 阅读详情

相关推荐

Net 内存溢出(System.OutOfMemoryException)的常见情况和处理方式总结

在什么情况下会出现OutOfMemonryException呢? 在我们试图新建一个对象时,而垃圾收集器又找不到任何可用内存时被抛出,这种情况下我们是可以捕获该 异常的; 另一种情况是,CLR需要内存时,而却系统却不能提供,也会抛出该异常. 但此时,我们的应用程序是不能捕获该错误的.

.NET Core GC 工作模式 (从其他地方抄来的,并添加一些自己在工作中注意的事项)

工作模式配置可以在项目文件中进行修改 <ServerGarbageCollection>false</ServerGarbageCollection> <ConcurrentGarbageCollection> ?<ConcurrentGarbageCollection> ?:这里选true 或者false. 也可以直接修改发布包中的ru...

野狼位位的博客 1136

微信小程序如何转云开发

小程序转云开发

zhangxiaobai___的博客 3913

理解C#垃圾回收机制

很多系统都有其自身的垃圾回收,其回收机制大体是相同的。它们使程序员从跟踪内存使用的繁重任务中解脱出来。虽然大多数回收器都要求应用程序不时地暂停从而释放不再使用的内存。但C#中的回收器效率还是很高的。      垃圾回收器的基本假定:         1.被分配内存空间的对象最有可能被释放。在方法执行时,就需要为该方法的对象分配内存空间,搜索最近分配的对象集合有助于花费最少的代价来尽可能多地释放内存

thy822的专栏 8468

BACnet/IP BBMD跨网段通信深度解析与最佳实践

本文介绍了在BACnet/IP网络中通过BBMD实现跨子网设备通信的关键配置方法。重点阐述了广播分发表(BDT)中掩码设置的重要性:使用子网掩码(255.255.255.0)会导致路由器拦截广播消息,而必须采用主机掩码(255.255.255.255)才能启用单播隧道机制,使BACnet广播消息封装为可路由的单播UDP包。文章详细说明了BDT条目的正确配置格式、BIP广播地址的作用,并提供了验证步骤,强调在跨网段场景中,正确设置掩码是确保设备发现和通信成功的关键因素。

零一架构师的专栏 1536

MDM主数据清洗和编码集成说明

绝大多数的企业业务系统是伴随业务发展而建设的,但随着业务发展单一系统难以支撑企业业务闭环。MDM主数据管理平台可以满足企业数据治理的需求,本文针对主数据清洗和编码集成进行梳理。...

数通畅联 3318

C#编程时应注意的性能处理

GC堆回收 那么除了通过new对象而达到代的阈(临界)值时,还有什么能够导致垃圾堆进行垃圾回收呢? 还可能windows报告内存不足、CLR卸载AppDomain、CLR关闭等其它特殊情况。 或者,我们还可以自己通过代码调用。 .NET有GC来帮助开发人员管理内存,并且版本也在不断迭代。GC帮我们托管内存,但仍然提供了System.GC类让开发人员能够轻微的协助管理。 这其中有一个可以清理内...

weixin_30292843的博客 257

RK3568 解决MIPI屏幕开机只显示uboot logo的问题

之前写过一篇文章介绍了RK3568驱动MIPI屏幕的方法,这次产品迭代,硬件改版,选型的新的屏幕了。所以再次开启调试屏幕的过程。本以为会和上次一样会轻车熟路,事情总是事与愿违,同样的路,遇到新的坑。

fhqlongteng的博客 5425

.Net iis 内存溢出(System.OutOfMemoryException)的常见情况和处理方式总结

最近项目上碰到了System.OutOfMemoryException 原因是页面上数据量大,缓存太多。 解决的三种方法:1)修改代码,降低缓存数据; 2)设置iis,应用程序池-回收条件-最大内存数(可以参考保存时候iis占用的内存) 3)设置iis,应用程序池-回收...

sunlibo111111的专栏 3316

ChatGPT Web转API工具:原理、部署与集成实战

在AI应用开发中,API(应用程序编程接口)是实现系统间通信和数据交换的核心技术,它通过标准化的请求响应机制,将复杂功能封装为可编程的服务。其工作原理基于HTTP协议,通过定义清晰的端点、方法和数据格式,使不同平台的应用能够高效集成AI能力。这一技术价值在于极大降低了开发门槛,促进了自动化流程和智能功能的快速落地,广泛应用于智能客服、内容生成和创意辅助等场景。本文聚焦于如何通过浏览器自动化技术,将ChatGPT的Web界面能力转化为标准API服务,其中涉及Playwright无头浏览器操作和会话Cookie

weixin_42533910的博客 1756

电机控制的智慧之眼:全面解析FOC状态观测器

摘要:本文深入探讨磁场定向控制(FOC)中的状态观测器技术,重点分析其在无传感器电机控制中的关键作用。文章详细介绍了滑模观测器(SMO)、扩展卡尔曼滤波(EKF)、龙贝格观测器和高频注入法等主流观测器的工作原理、技术特点及适用场景,其中SMO因结构简单、鲁棒性强成为当前主流方案。同时阐述了观测器与锁相环(PLL)结合的工程实践方案,并针对不同应用场景提供了观测器选型建议。文章还探讨了启动问题处理、参数敏感性等工程挑战的解决方案,以及混合观测器、AI应用等未来发展趋势。这些观测器技术使电机系统在无机械传感器情

汽车软件开发 273

C# 垃圾回收机制,.NET的GC机制有这样两个问题

GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。使用using语句可以简化资源管理。用到GC,命名空间System.GC;可以强制回收 GC.Collect()

yanglamei1962的博客 499

ABAP Channels 之 WebSocket 通信实践:用 ABAP Push Channel 打造实时应用

摘要:本文介绍了ABAP Push Channel(APC)技术,它通过WebSocket协议实现ABAP系统与外部客户端的实时双向通信。文章对比了传统HTTP轮询与WebSocket的差异,解释了APC的事件模型及其接口演进(从IF_APC_WS_EXTENSION到IF_APC_WSP_EXTENSION)。通过仓库拣货等实际案例,展示了APC在企业级应用中的价值,如实时监控、协作场景等。文中还详细阐述了ABAP引擎中WebSocket的交互模型,说明其资源优化设计原理,并提供了基础代码示例。该技术解决

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 293

GC垃圾回收

  我们在开发需求的时候,可能很少关注到垃圾回收,因为我们绝大多数的时候都是使用的托管资源,托管资源的内存回收.net已经帮我们做了,但是.net的内存回收不是实时的,所以我们还是要关注下.net的垃圾回收。 什么是GC GC即Garbage Collection,用一句话总结就是:.net内置的一种垃圾回收机制。   为什么要有GC 既然知道GC就是.net内置的垃圾回收,那为什么要有...

weixin_34335458的博客 97

MATLAB与图像处理(一):图像中结构张量的求法(Structure Tensor)

1.结构张量的作用:       能够区分图像中的平坦区域,边缘,角点; 2.图像中的结构张量的定义    1)是一个矩阵;    2)与图像的水平,垂直梯度有关,定义如下: 在MATLAB中,可以用如下语句求解:[Ix,Iy]=gradient(Image); 3)求出结构张量矩阵的行列式,和迹(矩阵对角线之和)   行列式:K=det(E);   迹:  

wenyusuran的专栏 5239

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

java gc 全称_GC是什么? 为什么要有GC?

GC是垃圾收集的意思(Gabage Collection),内存处理是编程人员容易出现问题的地方,忘记或者错误的内存回收会导致程序或系统的不稳定甚至崩溃,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,Java语言没有提供释放已分配内存的显示操作方法。GC是垃圾收集器。Java 程序员不用担心内存管理,因为垃圾收集器会自动进行管理。要请求垃圾收集,可以调用下面的方法...

weixin_42196750的博客 1703

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

GC垃圾回收之GC.KeepAlive方法

http://msdn.microsoft.com/zh-cn/library/system.gc.keepalive.aspx   http://www.cnblogs.com/ren700622/archive/2010/08/03/1791232.html   根据官方的说法,GC的垃圾回收是酱子地: 垃圾回收器搜索托管代码中引用的托管对象。 垃圾回收器尝试完成没...

weixin_33755847的博客 185

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

深入理解.NET内存回收机制

然后它在托管堆中寻找被根对象引用的对象(根对象就是全局,静态或处于活动中的局部变量以及寄存器指向的对象),找到后将它们加入一个有效对象的列表中,并在已经搜索过的对象中寻找是否有对象被新加入的有效对象引用。直到垃圾回收器检查完所有的对象后,就有一份根对象和根对象直接或间接引用了的对象的列表,而其它没有在表中的对象就被从内存中回收。当开始回收操作时,如前面提到的,垃圾回收器会找出根节点和根节点直接或间接引用了的对象,然后将这些对象转移到1等级托管堆中,并将0等级托管堆的指针移到最开始的位置以清除所有的对象。

biyusr的专栏 949

C#中的GC(Garbage Collector)垃圾回收机制

什么是GC? GC如其名,就是垃圾收集,当然这里仅就内存而言。系统自动遍历应用程序在Heap上动态分配的所有对象,通过识别它们是否被引用来确定哪些对象是已经死亡的、哪些仍需要被使用。已经不再被应用程序的root或者别的对象所引用的对象就是已经死亡的对象,即所谓的垃圾,需要被回收。C#数据类型分为引用类型和值类型,值类型保存在Stack上,随着函数的执行作用域执行完毕而自动出栈,所以这一类型的资源不...

菜鸡工坊 827

DotNetGuide内存管理:理解.NET中的GC机制

你是否曾遇到过.NET应用程序在运行一段时间后出现性能下降、响应变慢甚至崩溃的情况?是否在排查问题时对着内存占用曲线一筹莫展?作为.NET开发者,理解内存管理机制不仅是解决这类问题的关键,更是写出高性能、高稳定性代码的基础。本文将带你深入探索.NET中的垃圾回收(Garbage Collection,GC)机制,从原理到实践,全方位掌握内存管理的精髓。 读完本文,你将能够: - 理解.NET G...

gitblog_00704的博客 1146

C#面:.NET中的垃圾回收机制(GC)

在.NET中,垃圾回收(Garbage Collection,简称GC)是一种自动内存管理机制,它负责在程序运行时自动释放不再使用的内存资源,以避免内存泄漏,并提高程序的性能。

那个那个鱼的博客 930

.NET框架自动内存管理(4)

.NET框架自动内存管理(4) 4.GC类与垃圾回收控制 运行库提供GC类使应用程序能够控制垃圾回收器的行为。前面已经述及,垃圾回收周期是不确定的,由垃圾回收器引擎自动计算最佳的垃圾回收时间。在一个需要大量消耗内存的应用程序中,如果在某个确定的时间点上已经明确占用的内存不再需要,及时释放这些内存对提高应用程序的性能有显著的影响,这时就可以强制垃圾回收器执行回收周期。GC.Collect方法强制垃圾

OpenBAAN的专栏 1011

程序内存自动管理:.NET 的垃圾回收机制(4)

本文深入解析了.NET垃圾回收(GC)机制的工作原理。主要内容包括:1).NET内存的三级划分(新生代Gen 0/1、老年代Gen 2、大对象堆LOH)及其回收策略;2)GC的完整执行流程,从触发条件、标记阶段的可达性分析,到不同代的清理算法(复制算法、标记-清除/整理);3)C#对象引用场景辨析,重点说明集合存储仅为引用传递,以及参数传递的引用/值类型差异。文章通过具体示例,揭示了CLR如何自动管理内存,并给出开发者需注意的关键点,如集合引用保持、大对象堆碎片问题等。

zLongXiong的博客 968
上一篇: 调试.NET Web应用程序High CPU
下一篇: Windows同步机制原理及使用方法
WebDebug
博客等级 码龄14年 17粉丝 27原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值