.NET框架垃圾回收机制

推荐一款.net SqlSugar ORM 一站式扩展库 RuoVea.ExSugar 集成 DI 注册、DbContext/Repository 模式、审计字段自动填充、全局查询过滤器、工作单元事务、差异日志记录、SM2 连接字符串加密,覆盖从 DataExecuting AOP 到分页查询的完整数据访问层抽象。为 .NET 开发者提供 SqlSugar ORM 的开箱即用增强封装,通过 DI 容器统一管理数据库连接、审计字段、查询过滤器和事务边界。建议通过环境变量、密钥管理服务(如 Azure Key Vault)或加密的配置文件注入。重载以启用完整功能(差异日志、过滤器等)。 阅读详情

(三).NET框架垃圾回收机制
      .NET框架包含一个托管堆,所有的.NET语言在分配引用类型对象时都要使用它。像值类型这样的轻量级对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆。
     .NET框架中的垃圾回收器被称为分代的垃圾回收器(Generational Garbage Collector),也就是说被分配的对象划分为3个类别,或称为“代”。分别为0,1,2。0、1、2代对应的托管堆的初始化大小分别是256K,2M和10M。垃圾回收器在发现改变大小能够提高性能的话,会改变托管堆的大小。例如当应用程序初始化了许多小的对象,并且这些对象会被很快回收的话,垃圾回收器就会将第0代的托管堆变为128K,并且提高回收的频率。如果情况相反,垃圾回收器发现在第0代的托管堆中不能回收很多空间时,就会增加托管堆的大小。在应用程序初始化的之前,所有等级的托管堆都是空的。当对象被初始化的时候,他们会按照初始化的先后顺序被放入第0代的托管堆中。 
      最近被分配内存空间的对象被放置于第0代,因为第0代很小,小到足以放进处理器的二级(L2)缓存,所以第0代能够为我们提供对其中对象的快速存取。经过一轮垃圾回收后,仍然保留在第0代中的对象被移进第1代中,再经过一轮垃圾内存回收后,仍然保留在第1代中的对象则被移进第2代中。第2代包含了生存期较长的对象,这些对象至少经过了两轮回收。
      C#程序为一个对象分配内存时,托管堆几乎可以立即返回新对象所需的内存,托管堆之所以能有这样高效的内存分配性能是由于托管堆较为简单的数据结构。托管堆类似于简单的字节数组,有一个指向第一个可用内存空间的指针。
      在某块被某对象所请求时,上述指针值就会返回给调用函数,而指针会重新调整至指向下一个可用的内存空间。分配一个托管内存块只比递增一个指针的值稍微复杂一点。这也是托管堆所优化的性能之一。在一个不需太多垃圾回收的应用程序中,托管堆的表现会优于传统的堆。
      由于这个线性的内存分配方法的存在,在C#应用程序中同时分配的对象在托管堆上通常会被分配成彼此相邻。着安排和传统的堆内存分配完全不同,传统的堆内存分配是基于内存块大小的。例如,两个同时分配的对象在堆上的位置可能相距很远,从而降低了缓存的性能。因此虽然内存分配很快,但在一些比较重要的程序中,第0代中的可用内存很有可能会彻底被消耗光。记住,第0代小到可以装进L2缓冲区,并且没有被使用的内存不会被自动释放。当第0代中没有可以分配的有效内存时,就会在第0代中触发一轮垃圾回收,在这轮垃圾回收中将删除所有不再被引用的对象,并将当前正在使用中的对象移至第1代。针对第0代的垃圾回收是最常见的回收类型,而且速度很快。在第0代的垃圾内存回收不能有效的请求到充足的内存时,就启动第1代的垃圾内存回收。第2代的垃圾内存回收要作为最后一种手段而使用,当且仅当第1代和第0代的垃圾内存回收不能被提供足够内存时进行。如果各代都进行了垃圾回收后仍没有可用的内存,就会引发一个OutOfMemeryException异常 。

 http://www.yesky.com/401/1656401.shtml

[前言:].Net平台提供了许多新功能,这些功能能够帮助程序员生产出更高效和稳定的代码。其中之一就是垃圾回收器(GC)。这篇文章将深入探讨这一功能,了解它是如何工作的以及如何编写代码来更好地使用这一.Net平台提供的功能。

  .Net中的内存回收机制

  垃圾回收器是用来管理应用程序的内存分配和释放的。在垃圾回收器出现以前,程序员在使用内存时需要向系统申请内存空间。有些语言,例如Visual Basic,可以自动完成向系统申请内存空间的工作。但是在诸如Visual C++的语言中要求程序员在程序代码中申请内存空间。如果程序员在使用了内存之后忘了释放内存,则会引起内存泄漏。但是有了垃圾回收器,程序员就不必关心内存中对象在离开生存期后是否被释放的问题。当一个应用程序在运行的时候,垃圾回收器设置了一个托管堆。托管堆和C语言中的堆向类似,但是程序员不需要从托管堆中释放对象,并且在托管堆中对象的存放是连续的。

  每次当开发人员使用 new 运算符创建对象时,运行库都从托管堆为该对象分配内存。新创建的对象被放在上次创建的对象之后。垃圾回收器保存了一个指针,该指针总是指向托管堆中最后一个对象之后的内存空间。当新的对象被产生时,运行库就知道应该将新的对象放在内存的什么地方。同时开发人员应该将相同类型的对象放在一起。例如当开发人员希望向数据库写入数据的时侯,首先需要创建一个连接对象,然后是Command对象,最后是DataSet对象。如果这些对象放在托管堆相邻的区域内,存取它们就非常快。

  当垃圾回收器的指针指向托管堆以外的内存空间时,就需要回收内存中的垃圾了。在这个过程中,垃圾回收器首先假设在托管堆中所有的对象都需要被回收。然后它在托管堆中寻找被根对象引用的对象(根对象就是全局,静态或处于活动中的局部变量以及寄存器指向的对象),找到后将它们加入一个有效对象的列表中,并在已经搜索过的对象中寻找是否有对象被新加入的有效对象引用。直到垃圾回收器检查完所有的对象后,就有一份根对象和根对象直接或间接引用了的对象的列表,而其它没有在表中的对象就被从内存中回收。

  当对象被加入到托管堆中时,如果它实现了finalize()方法,垃圾回收器会在它的终结列表(Finalization List)中加入一个指向该对象的指针。当该对象被回收时,垃圾回收器会检查终结列表,看是否需要调用对象的finalize()方法。如果有的话,垃圾回收器将指向该对象的指针加入一个完成器队列中,该完成器队列保存了那些准备调用finalize()方法的对象。到了这一步对象还不是真正的垃圾对象。因此垃圾回收器还没有把他们从托管堆中回收。

  当对象准备被终结时,另一个垃圾回收器线程会调用在完成器队列中每个对象的finalize()方法。当调用完成后,线程将指针从完成器队列中移出,这样垃圾回收器就知道在下一次回收对象时可以清除被终结的对象了。从上面可以看到垃圾回收机制带来的很大一部分额外工作就是调用finalize()方法,因此在实际编程中开发人员应该避免在类中实现finalize()方法。

  对于finalize()方法的另一个问题是开发人员不知道什么时候它将被调用。它不像C++中的析构函数在删除一个对象时被调用。为了解决这个问题,在.Net中提供了一个接口IDisposable。微软建议在实现带有fianlize()方法的类的时侯按照下面的模式定义对象:

public   class  Class1 : IDisposable 
{
 
public Class1()
 
{
 }


 
~Class1 ()
 
{
  
//垃圾回收器将调用该方法,因此参数需要为false。
  Dispose (false);
 }


 
//该方法定义在IDisposable接口中。
 public void Dispose ()
 
{
  
//该方法由程序调用,在调用该方法之后对象将被终结。
  
//因为我们不希望垃圾回收器再次终结对象,因此需要从终结列表中去除该对象。
  GC.SuppressFinalize (this);
  
//因为是由程序调用该方法的,因此参数为true。
  Dispose (true);
 }


 
//所有与回收相关的工作都由该方法完成
 private void Dispose(bool disposing)
   
{
  
lock(this//避免产生线程错误。
  {
   
if (disposing)
   
{
    
//需要程序员完成释放对象占用的资源。
   }


  
//对象将被垃圾回收器终结。在这里添加其它和清除对象相关的代码。
 }

}

}

  现在我们了解了垃圾回收器工作的基本原理,接下来让我们看一看垃圾回收器内部是如何工作的。目前有很多种类型的垃圾回收器。微软实现了一种生存期垃圾回收器(Generational Garbage Collector)。生存期垃圾回收器将内存分为很多个托管堆,每一个托管堆对应一种生存期等级。生存期垃圾回收器遵循着下面的原则:

  新生成的对象,其生存期越短;而对象生成时间越长的对象,其生存期也就越长。对于垃圾回收器来说,回收一部分对象总是比回收全部对象要快,因此垃圾回收器对于那些生存期短的对象回收的频率要比生存期长的对象的回收频率高。

  .Net中的垃圾回收器中目前有三个生存期等级:0,1和2。0、1、2等级对应的托管堆的初始化大小分别是256K,2M和10M。垃圾回收器在发现改变大小能够提高性能的话,会改变托管堆的大小。例如当应用程序初始化了许多小的对象,并且这些对象会被很快回收的话,垃圾回收器就会将0等级的托管堆变为128K,并且提高回收的频率。如果情况相反,垃圾回收器发现在0等级的托管堆中不能回收很多空间时,就会增加托管堆的大小。

  在应用程序初始化的之前,所有等级的托管堆都是空的。当对象被初始化的时候,他们会按照初始化的先后顺序被放入等级为0的托管堆中。在托管堆中对象的存放是连续的,这样使得托管堆存取对象的速度很快,因为托管对不必对内存进行搜索。垃圾回收器中保存了一个指针指向托管堆中最后一个对象之后的内存空间。图一中显示了一个包含四个对象的0等级的托管堆。


图一 包含四个对象的托管堆

  当0等级托管堆被对象填满后,例如候程序初始化了新的对象,使0等级托管堆的大小超过了256K,垃圾回收器会检查托管堆中的所有对象,看是否有对象可以回收。当开始回收操作时,如前面提到的,垃圾回收器会找出根节点和根节点直接或间接引用了的对象,然后将这些对象转移到1等级托管堆中,并将0等级托管堆的指针移到最开始的位置以清除所有的对象。同时垃圾回收器会压缩1等级托管堆以保证所有对象之间没有内存空隙。当1等级托管堆满了之后,会将对象转移到2等级的托管堆。

  例如在图一之后,垃圾回收器开始回收对象,假定D对象将被回收,同时程序创建了E和F对象。这时候托管堆中的对象如图二所示。


图二 回收对象后的0等级和1等级托管堆

  然后程序创建了新的对象G和H,再一次触发了垃圾回收器。对象E将被回收。这时候托管堆中的对象如图三所示。



  生存期垃圾回收器的原则也有例外的情况。当对象的大小超过84K时,对象会被放入"大对象区"。大对象区中的对象不会被垃圾回收器回收,也不会被压缩。这样做是为了强制垃圾回收器只能回收小对象以提高程序的性能。

  控制垃圾回收器

  在.Net框架中提供了很多方法使开发人员能够直接控制垃圾回收器的行为。通过使用GC.Collect()或GC.Collect(int GenerationNumber)开发人员可以强制垃圾回收器对所有等级的托管堆进行回收操作。在大多数的情况下开发人员不需要干涉垃圾回收器的行为,但是有些情况下,例如当程序进行了非常复杂的操作后希望确认内存中的垃圾对象已经被回收,就可以使用上面的方法。另一个方法是GC.WaitForPendingFinalizers(),它可以挂起当前线程,直到处理完成器队列的线程清空该队列为止。

  使用垃圾回收器最好的方法就是跟踪程序中定义的对象,在程序不需要它们的时候手动释放它们。例如程序中的一个对象中有一个字符串属性,该属性会占用一定的内存空间。当该属性不再被使用时,开发人员可以在程序中将其设定为null,这样垃圾回收器就可以回收该字符串占用的空间。另外,如果开发人员确定不再使用某个对象时,需要同时确定没有其它对象引用该对象,否则垃圾回收器不会回收该对象。

  另外值得一提的是finalize()方法应该在较短的时间内完成,这是因为垃圾回收器给finalize()方法限定了一个时间,如果finalize()方法在规定时间内还没有完成,垃圾回收器会终止运行finalize()方法的线程。在下面这些情况下程序会调用对象的finalize()方法:

   0等级垃圾回收器已满

   程序调用了执行垃圾回收的方法

   公共语言运行库正在卸载一个应用程序域

   公共语言运行库正在被卸载

机器学习篇-KNN算法实现鸢尾花模型和手写数字识别模型 一个样本最相似的k个样本中的大多数属于某一个类别, 则该样本也属于这个类别类似于: 近朱者赤近墨者黑回归机器建模流程:数据获取数据基本处理(空值和非法值)特征工程(特征提取, 特征预处理(归一化, 标准化), 特征降维, 特征选择, 特征组合)模型训练模型预测模型评估# 1. 定义函数 dm01_load_iris(), 用于加载 鸢尾花数据集, 并简单查看下数据集的各部分.# 1. 加载数据集.​# 2. 打印iris_data, 发现其实是1个: 字典.​。 阅读详情

相关推荐

CAD 线宽“隐身”之谜:从 LWDISPLAY系统变量到 .NET 二次开发实战

<think>我们根据用户要求,基于提供的文章生成摘要,不超过150字。文章内容是关于CAD线宽显示问题的分析与解决,包括LWDISPLAY变量、.NET开发中的处理等。摘要需简洁概括核心内容,控制在150字内。</think>本文剖析CAD线宽“显示不粗”的根因——LWDISPLAY系统变量默认为0,导致忽略线宽渲染;并给出标准排查步骤与.NET C#二次开发中精准控制线宽及开启显示的代码示例,强调通过Application.SetSystemVariable设置变量,确保插件环境下线宽效果即时可见。

ultramand的博客,重生之我在cad上二次开发猛猛敲C#代码~ 17

垃圾回收:在微软.NET框架自动内存管理

这个一个解释.NET框架垃圾回收机制的一个很好的文章,希望能够帮助到初学者。这是Jeffrey Richter 2000年在MSDN杂志上的文章。

使用Simulink进行四足机器人的非线性动力学建模与仿真

四足机器人的运动包含多个自由度(DOF),包括腿部的摆动和支撑阶段的动力学特性。由于存在接触力、摩擦以及不同地形的影响,四足机器人的动力学行为呈现出高度的非线性特征。因此,构建准确的非线性动力学模型对于研究机器人的运动学和动力学特性至关重要。定义代表机器人状态的变量,如位置、速度、角度和角速度。matlab深色版本通过本指南,我们介绍了如何基于Simulink搭建一个四足机器人的非线性动力学模型,并进行了详细分析。

xiaoheshang_123的博客 287

.NET框架垃圾回收机制透析(转载)

.NET框架包含一个托管堆,所有的.NET语言在分配引用类型对象时都要使用它。像值类型这样的轻量级对象始终分配在栈中,但是所有的类实例和数组都被生成在一个内存池中,这个内存池就是托管堆。 .NET框架中的垃圾回收器被称为分代的垃圾回收器(Generational Garbage Collector),也就是说被分配的对象划分为3个类别,或称为“代”。分别为0,1,2。0、1、2代对应...

weixin_30299709的博客 127

.NET垃圾回收机制详解与心得体会

这几天对垃圾回收机制有了较为透彻的理解,在此与朋友们分享一下。首先,让我们来了解下什么是垃圾回收机制。为了使程序员从跟踪内存使用的繁重任务中解脱出来,利用充分的时间去完成业务逻辑,因此产生了垃圾回收机制,虽然大多数的垃圾回收器都要求应用程序不停地暂停从而释放出不在使用的资源,但是.NET垃圾回收器效率依然很高。垃圾回收器的基本思想:      寻找不再使用的对象,将他们从内存中删除,

zjybushiren88888的专栏 1085

.net framework 垃圾回收机制

首先,让我们来了解下什么是垃圾回收机制。为了使程序员从跟踪内存使用的繁重任务中解脱出来,利用充分的时间去完成业务逻辑,因此产生了垃圾回收机制,虽然大多数的垃圾回收器都要求应用程序不停地暂停从而释放出不在使用的资源,但是.NET垃圾回收器效率依然很高。垃圾回收器的基本思想:      寻找不再使用的对象,将他们从内存中删除,并压实托管堆以释放不在使用的对象所占用的内存。在堆被压实之后,

zenolee的专栏 582

第一章:.NET Framework和C#

C#概述

xfeng20的博客 170

Java与C#的垃圾回收机制

(一)垃圾回收器的基本假定(1)最近被分配内存空间的对象最有可能需要被释放。在方法被执行前,通常需要为该方法所使用到的对象分配内存空间,搜索最近被分配的对象集合有助于花费最少的工作来释放进可能多的空闲内存空间。(2)生命期最长的对象需要释放的可能性最小。在通过几轮垃圾回收后仍然存在的对象不大可能是那种能够在下一轮回收中被释放的临时对象,搜索这些内存块往往要进行大量的工作,却只能释放很小一部

盗版陈小春 3732

.Net垃圾回收机制原理和垃圾回收算法简单执行流程

  垃圾回收机制使程序员不需要再关注什么时候释放内存,释放内存这件事儿完全由GC做了,为程序员省去了不少的麻烦。但是,作为一个"不拖控件"的.Net程序员很有必要理解垃圾回收是如何工作的。下面我们来看下.Net是如何分配和管理托管内存的,之后再一步一步描述垃圾回收器工作的算法。 让我们想一下,每一个程序都要使用内存资源:例如屏幕显示,网络连接,数据库资源等等。实际上,在一个面向对象环境中,...

weixin_30437847的博客 131

基于.NET的Windows窗体编程之WinForms文件监控

在实际应用开发中,有时候需要操作本地文件或文件夹,还需监控磁盘目录的变化,比如当新增或删除文件时,提示变更信息,以起到警示作用。合理的文件监控,还可以起到事半功倍的效果,今天我们以一些简单的小例子,简述基于.NET的Windows窗体编程中FileSystemWatcher控件的相关应用,仅供学习分享使用,如有不足之处,还请指正。

绳锯木断,水滴石穿,专心写文,无问西东!!! 271

基于.NET的Windows窗体编程之WinForms滑块与上下控件

<think>我们根据内容生成摘要,≤150字。提取核心:介绍WinForms中TrackBar、DomainUpDown、NumericUpDown三个控件的用法,强调合理利用提高用户体验、节省空间。需简洁。</think>本文介绍.NET WinForms中三个实用控件:DomainUpDown用于上下选择列表项,NumericUpDown用于数值增减,TrackBar滑块直观调值。合理布局可提升体验、节省空间,附属性与事件示例。

绳锯木断,水滴石穿,专心写文,无问西东!!! 163

.NET 线程安全集合与并发数据结构深度实战:从 lock 到无锁

<think>我们需要根据用户提供的文本,生成一个不超过150字的文章摘要。用户给出了这篇关于.NET并发编程的长文,内容涵盖ConcurrentDictionary、线程安全集合、锁、原子性等。需要提炼核心,控制字数。 我们需要先理解文章主旨:通过两个事故(计数器丢更新、字典死循环)引出“线程安全集合只保证内部结构安全,不保证复合逻辑正确”。然后深入讲解ConcurrentDictionary内部、AddOrUpdate的工厂陷阱、GetOrAdd的副作用问题,并提供解决方案(StrongBox+Inte

一个热爱编程的人 216

程序提示 CLR 加载异常打不开?.NET 运行时初始化失败完整修复方案

程序需要的.NET 版本和系统已安装版本不匹配,程序需要.NET3.5,系统只装了.NET4.8,或是反过来需要.NET6/7 桌面运行时却没有部署。CLR 全称公共语言运行时,是.NET 程序的执行引擎,报错本质是系统无法正常初始化.NET 运行环境,不是单纯缺失某一个 dll 文件。不想一步步手动排查的朋友,可以打开暴喵修复匠,输入暗号【白猫 01】,工具会修复 VC++ 以及相关系统前置依赖,排除运行库损坏带来的 CLR 加载障碍。游戏路径尽量使用纯英文,中文、特殊符号会干扰.NET 程序集读取。

wind10032的博客 204

技术速递|今天我将……现代化一个 .NET 应用

现在,我们已经成功将这个应用升级到了 .NET 10。如果你愿意,还可以继续使用 GitHub Copilot .NET 现代化工具,将这个应用进一步迁移到 Azure。面向初学者的 .NET 现代化指南。课程会更加深入地介绍应用的升级和迁移流程。欢迎前往了解。当然,也欢迎在这篇文章下留言,告诉我们你的使用体验。查看面向初学者的 .NET 现代化指南课程!

MicrosoftReactor的博客 228

推荐一款.net工具RuoVea.ExUtil

/ 当前 UTC+8 区 Unix 时间戳(秒)// Unix 时间戳 → DateTime// DateTime → Unix 时间戳(秒)⚠️已知缺陷硬编码,仅适用于 UTC+8(中国时区)场景,非东八区环境需自行处理。文件: Validate/Attributes/EmailAttribute.cs构造参数: msg(自定义错误消息)实际: 构造函数接收 msg 参数后未使用,始终返回硬编码的英文错误消息。

A_nanda的博客 332

.NET 高性能内存编程深度实战:从 Span\<T\> 到 Pipelines,把船岸报文解析的分配数砍到零

在解析 200 字段的完整报文时差距是数量级的——字符串版本分配 3MB,Span 版本如果全程用 Span 比较、只有落库时才物化关键字段,分配 < 50KB。PMS 船岸同步的 TCP 报文网关重构为 Pipelines 后,缓冲相关分配归零,且粘包/半包的边界 bug(重构前三年修过 5 次)从根上消失。30 艘船整点并发上报,每秒分配 186MB,Gen0 回收频率拉满,GC 挂起线程(即使是后台 GC 也有短暂暂停),P99 毛刺就这么来的。冷路径无所谓(代码可读性优先),每秒执行万次的热点用。

一个热爱编程的人 433

微软技术周报·2026-08-24~2026-08-31——.NET11 Preview7最后一个预览版、AI Agent基础设施全面走向GA与治理化

微软技术周报:.NET 11功能冻结前最后一版Preview 7发布,C#带标签break、union模式改进,Runtime Async性能飞跃;AI Agent全面GA与治理化(Agent Plugins 1.0、VS 2026组织级Agent、Copilot Studio迁移Entra Agent ID);Windows 11 26H2进入Release Preview,预计9月底推送。

海盗Sharp的博客 727

Linux 字符设备驱动框架详细介绍

主要介绍了Linux 字符设备驱动框架详细介绍的相关资料,字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,这里提供简单的实例,需要的朋友可以参考下

上一篇: SQL语句 传自定义表到临时表
下一篇: 类的继承,大家试试看,想得是否和结果对得上。
leezile
博客等级 码龄20年 0粉丝 13原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值