浅析C#中的托管非托管堆栈与垃圾回收

Posted ProMer_Wang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析C#中的托管非托管堆栈与垃圾回收相关的知识,希望对你有一定的参考价值。

浅析托管、非托管堆栈与C#垃圾回收

文章目录

一、C#托管服务下的几个重要概念:

1、 托管代码:

1、使用 .NET 时,我们经常会遇到“托管代码”这个术语。

2、简而言之,托管代码就是执行过程交由运行时管理的代码。

2、CLR阶段:

1、在托管服务下,相关的运行时称为公共语言运行时 (CLR)。

2、不管使用的是哪种实现(例如 Mono、.NET Framework 或 .NET Core/.NET 5+)。 CLR 负责提取托管代码、将其编译成机器代码,然后执行它。

3、 除此之外,运行时还提供多个重要服务,例如自动内存管理(也也就是后面提到GC机制)、安全边界、类型安全,等等。

3、非托管代码:

1、概念:

公共语言运行库环境的外部,由操作系统直接执行的代码

2、区别:

托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务,它与托管代码不同,后者从公共语言运行库中获得这些服务,而非托管代码是在运行库之外运行的代码。例如COM 组件、ActiveX 接口和 Win32 API 函数都是非托管代码的示例。

3、C#官方文档做出的说明:

在非托管环境中,程序员需要亲自负责处理相当多的事情。 实际的程序在本质上是操作系统 (OS) 载入内存,然后启动的二进制代码。 其他任何工作 - 从内存管理到安全考虑因素 - 对于程序员来说是一个不小的负担。

托管代码是使用可在 .NET 上运行的一种高级语言(例如 C#、Visual Basic、F# 等)编写的。 使用相应的编译器编译以这些语言编写的代码时,无法获得机器代码, 而是获得中间语言代码,然后运行时会对其进行编译并将其执行。 C++ 是这条规则的一个例外,因为它也能够生成可在 Windows 上运行的本机非托管二进制代码。

4、中间语言(IL、CIL、MSIL):

1、简称:

什么是“中间语言”(简称 IL)?中间语言有时也称为公共中间语言 (CIL) 或 Microsoft 中间语言 (MSIL)。

2、概念:

中间语言其实意如其名,意思就是一个从高级语言转成机器码中间的过度语言,因为大体的流程其实是这么一回事

①开发者使用的高级语言编写功能实现代码

---->②LC(Language Complier我们通常称之为解释器,也就是编译器内置的解释器)

---->③MSIL(IL\\CIL)---->通过CLR和JIT实时编译运行将IL代码转成对应的机器码(即二进制码)

---->④机器通过执行机器码实现对应的功能效果;

(看到了这,我恍然大悟,大学科目原来是这么用的啊,可算是圆回来了hhhhhhh,“*(不能明说的字)” 原来硬件是这么用的啊,寄存器,CPU其实说白了就是一个机器码执行器,这么说其实有问题,对于cpu大家可以参考这个链接:cpu的相关概念)

5、托管代码互操作性

当然,CLR 允许越过托管与非托管环境之间的边界,同时,即使在基类库中,也有很多代码可以做到这一点。 这称为互操作性,简称 interop。 例如,使用这些机制可以包装某个非托管库以及调用该库。 但是,请务必注意,如果采取这种方法,当代码越过运行时的边界时,实际的执行管理将再次交接到托管代码,因而需要遵守相同的限制。

与此类似,C# 语言可让你利用所谓的不安全上下文(指定执行过程不由 CLR 管理的代码片段),在代码中直接使用非托管构造,例如指针。

至于托管代码的互操作性,我目前还不是很明确但是查究到了这么一篇文章【转帖】 CLR 全面透彻解析:托管和本机代码互操作性,讲述的挺好的,那我不经发出了疑问,那是不是有的Unity的C#的热更方案就有用到这个托管代码的互操作性呢?提出一个问题(这个问题以后有待考究),有了解或者使用过这个大佬的博友看到过,希望给出对应的解释和回答,或者在评论区贴上对应的链接,不甚感激!!!!

二、垃圾回收机制:

杂谈:

以前谈到C#中的GC(Garbage Collector-垃圾收集器),仅就内存而言的垃圾收集。我们在自己心中都有一个概念就是C#中有这么一种内存管理方案来帮助开发者自动取管理内存,释放掉我们没有使用的内存资源,从而把内存资源分配给我们正在使用的对象上去;

但是当我回过头来了解这个垃圾回收机制的时候,我发现了一个很有意思的事情,当我去查询相关的资料的时候我发现,这其中的原理真的是一环扣一环,还环环不一样,我的内心:(TM*燃起来了!)。相反导致这个有意思的方法勾起了我的探索欲,这到底是个什么玩意儿,究竟神奇在哪;

1、垃圾回收的基本概念:

.NET 的垃圾回收器管理应用程序的内存分配和释放。 每当有对象新建时,公共语言运行时都会从托管堆为对象分配内存。 只要托管堆中有地址空间,运行时就会继续为新对象分配空间。 不过,内存并不是无限的。 垃圾回收器最终必须执行垃圾回收来释放一些内存。 垃圾回收器的优化引擎会根据所执行的分配来确定执行回收的最佳时机。 执行回收时,垃圾回收器会在托管堆中检查应用程序不再使用的对象,然后执行必要的操作来回收其内存。

概括:就是GC会帮你自动管理内存,分配内存,回收内存,采用的就是对应的GC的算法。

2、垃圾回收带来的优点:

垃圾回收器具有以下优点:

  • 开发人员不必手动释放内存。
  • 有效分配托管堆上的对象。
  • 回收不再使用的对象,清除它们的内存,并保留内存以用于将来分配。 托管对象会自动获取干净的内容来开始,因此,它们的构造函数不必对每个数据字段进行初始化。
  • 通过确保对象不能自己使用分配给另一个对象的内存来提供内存安全。
  • 在公共语言运行时 (CLR) 中,垃圾回收器 (GC) 用作自动内存管理器。 垃圾回收器管理应用程序的内存分配和释放。 对于使用托管代码的开发人员而言,这就意味着不必编写执行内存管理任务的代码。 自动内存管理可解决常见问题,例如,忘记释放对象并导致内存泄漏,或尝试访问已释放对象的内存。

3、垃圾回收机制具体做了什么:

垃圾回收主要有这么几个阶段:

  • 标记阶段,找到并创建所有活动对象的列表。
  • 重定位阶段,用于更新对将要压缩的对象的引用。
  • 压缩阶段,用于回收由死对象占用的空间,并压缩幸存的对象。 压缩阶段将垃圾回收中幸存下来的对象移至段中时间较早的一端。
  • 因为第 2 代回收可以占用多个段,所以可以将已提升到第 2 代中的对象移动到时间较早的段中。 可以将第 1 代幸存者和第 2 代幸存者都移动到不同的段,因为它们已被提升到第 2 代。

垃圾回收器使用以下信息来确定对象是否为活动对象:

  • 由实时 (JIT) 编译器和堆栈查看器提供的堆栈变量。 JIT 优化可以延长或缩短报告给垃圾回收器的堆栈变量内的代码的区域。
  • 指向托管对象且可由用户代码或公共语言运行时分配的句柄。
  • 应用程序域中可能引用其他对象的静态对象。 每个应用程序域都会跟踪其静态对象。

然而这里面所指代的第几代第几代实际上就是C#官方文档所讲述到的幸存与提升,感兴趣的朋友可以到官方文档里面了解其中的细节,因为牵扯的内容太多太杂,我觉得太杂反而不利于去理解其中的核心过程,就不做赘述了,请网友自行查阅吧。

在查询资料的时候看到了其他博主形象的把官方文档的内容通过示意图画出来了,还挺有意思的:

大概传达的一个意思就是:

GC:“你们不用这块内存了是吧?那我把你们排挤出去了奥,你们谁还要续租使用的,可以继续保留,但是前提是此时已经不是原来的内存了,已经是一块新分配的内存了”;

为了方便了解其中的内容,可以试着用租房与房东之间的关系来具体理解;

该图片转载自:
C#关于托管资源GC垃圾回收与非托管资源继承 IDisposable接口 实现 Dispose方法释放非托管资源

该图片转载自:

C#垃圾回收机制GC

4、垃圾回收机制的注意事项:

  • 对于应用程序创建的大多数对象,可以依赖垃圾回收器自动执行必要的内存管理任务。 但是,非托管资源需要显式清除。

  • 最常用的非托管资源类型是包装操作系统资源的对象,例如,文件句柄、窗口句柄或网络连接。

  • 创建封装非托管资源的对象时,建议在公共 Dispose 方法中提供必要的代码以清理非托管资源。 通过提供 Dispose 方法,对象的用户可以在使用完对象后显式调用释放其内存。

  • 使用using和try/finally清理资源,

  • 所有非托管资源的类型必须显式地使用IDisposable接口的Dispose()来释放。所有封装或使用了非托管资源的类型都实现了IDisposable,这些类型在终结器中也会调用Dispose(),以便在忘记的时候仍能保证正常释放资源。不过这些资源将在内存中停留更长时间,应用程序也会变成资源消耗大户。(此条目收录自C# 学习笔记 .Net资源管理)

    若想使用一个可销毁的对象,那么using语句能够以最简单的方式保证你的对象可以正常销毁。using语句将生成一个try/finally块,包裹住分配的对象。这两段代码生成的IL完全一致:

参考链接:

cpu的相关概念

C#关于托管资源GC垃圾回收与非托管资源继承 IDisposable接口 实现 Dispose方法释放非托管资源

C# 学习笔记 .Net资源管理

C#垃圾回收机制GC

C#官方文档之垃圾回收的托管服务相关概念说明

C#官方文档之垃圾回收的相关概念文档说明

【转帖】 CLR 全面透彻解析:托管和本机代码互操作性
c#中的托管、非托管、垃圾回收、using


以上呢,便是我对托管、非托管堆栈与C#垃圾回收作出的简要的描述,希望能够对你有所帮助~ 也希望你能够点赞、评论吖~ 你们的点赞、评论就是我前进的动力!


作者:ProMer_Wang

链接:https://blog.csdn.net/qq_43801020/article/details/127601735

本文为ProMer_Wang的原创文章,著作权归作者所有,转载请注明原文出处,欢迎转载!

以上是关于浅析C#中的托管非托管堆栈与垃圾回收的主要内容,如果未能解决你的问题,请参考以下文章

C#中的IDisposable模式用法详解

C#中的IDisposable模式用法详解

IDisposable

C# 的析构

C#内存管理与垃圾回收

浅析CLR的GC(垃圾回收器)