当前位置: 首页 > article >正文

.net 之内存回收

前言

一些基本概念如下:

托管代码

托管代码就是执行过程交由运行时管理的代码。 在这种情况下,相关的运行时称为公共语言运行时 (CLR),不管使用的是哪种实现(例如 Mono、.NET Framework 或 .NET Core/.NET 5+)。 CLR 负责提取托管代码、将其编译成机器代码,然后执行它。 除此之外,运行时还提供多个重要服务,例如自动内存管理、安全边界和类型安全。

非托管代码

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

非托管资源

使用完非托管资源后,必须显式释放这些资源。 最常用的非托管资源类型是包装操作系统资源的对象,如文件、窗口、网络连接或数据库连接

内存分配

每个进程都有其自己单独的虚拟地址空间。 同一台计算机上的所有进程共享相同的物理内存和页文件(如果有)

32 位计算机上的每个进程都具有 2 GB 的用户模式虚拟地址空间

  • 可能会存在虚拟地址空间碎片,这意味着地址空间中存在一些被称为孔的可用块。 当请求虚拟内存分配时,虚拟内存管理器必须找到满足该分配请求的足够大的单个可用块。 即使有 2 GB 可用空间,2 GB 分配请求也会失败,除非所有这些可用空间都位于一个地址块中。

  • 如果没有足够的可供保留的虚拟地址空间或可供提交的物理空间,则可能会用尽内存。

堆(Heap)

  • 堆内存是动态分配的。通过调用newmalloc等方法显式地分配内存,需要显式释放(如freedelete)。堆适合存储更大、更复杂的数据结构(如对象、数组)。
  • 内存管理更加灵活,但需要手动管理或依赖垃圾回收器。
托管堆

初始化新进程时,运行时会为进程保留一个连续的地址空间区域。 这个保留的地址空间被称为托管堆。 托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。 最初,该指针设置为指向托管堆的基址。 托管堆上包含了所有引用类型。 应用程序创建第一个引用类型时,将为托管堆的基址中的类型分配内存。 应用程序创建下一个对象时,垃圾回收器在紧接第一个对象后面的地址空间内为它分配内存。

从托管堆中分配内存要比非托管内存分配速度快。 由于运行时通过为指针添加值来为对象分配内存,所以这几乎和从堆栈中分配内存一样快。

大对象堆(LOH)

大对象堆包含大小不少于 85,000 (约0.6M)个字节的对象,这些对象通常是数组。 非常大的实例对象是很少见的。

这个阀也是可以进行设置的

  • 该值可能由运行时限制为当前配置的最大可能大小。 可以通过 GC.GetConfigurationVariables() API 检查运行时使用的值

栈(Stack)

  • 栈内存是自动分配的。函数调用时,局部变量在栈上分配,函数结束后自动释放。栈内存通常很小,适合存放小的数据(如基本类型、指针)。
  • 内存分配方式是LIFO(后进先出),即最后放入的变量会最先释放。

GC回收的过程

垃圾回收器在执行回收时,会释放应用程序不再使用的对象的内存。 它通过检查应用程序的根来确定不再使用的对象。 每个应用程序都有一组根。 每个根或者引用托管堆中的对象,或者设置为空。 应用程序的根包含线程堆栈上的静态字段、局部变量和参数以及 CPU 寄存器。 垃圾回收器可以访问由实时 (JIT) 编译器和运行时维护的活动根的列表。 垃圾回收器对照此列表检查应用程序的根,并在此过程中创建一个图表,在其中包含所有可从这些根中访问的对象。

不在该图表中的对象将无法从应用程序的根中访问。 垃圾回收器会考虑无法访问的对象垃圾,并释放为它们分配的内存。 在回收中,垃圾回收器检查托管堆,查找无法访问对象所占据的地址空间块。 发现无法访问的对象时,它就使用内存复制功能来压缩内存中可以访问的对象,释放分配给不可访问对象的地址空间块。 在压缩了可访问对象的内存后,垃圾回收器就会做出必要的指针更正,以便应用程序的根指向新地址中的对象。 它还将托管堆指针定位至最后一个可访问对象之后。 请注意,只有在回收发现大量的无法访问的对象时,才会压缩内存。 如果托管堆中的所有对象均未被回收,则不需要压缩内存。

为了改进性能,运行时为单独堆中的大型对象分配内存。 垃圾回收器会自动释放大型对象的内存。 但是,为了避免移动内存中的大型对象,不会压缩此内存。

代数

垃圾回收主要在回收短生存期对象时发生。 为优化垃圾回收器的性能,将托管堆分为三代:第 0 代、第 1 代和第 2 代,因此它可以单独处理长生存期和短生存期对象。

第0代:

新分配的小对象构成新一代对象,并隐式地成为第 0 代集合,如果是大对象则为大型对象堆 (LOH),也称为第 3 代。

第1代:

如果第 0 代托管堆的回收没有回收足够的内存供应用程序创建新对象,垃圾回收器就会先执行第 1 代托管堆的回收,然后再执行第 2 代托管堆的回收。 第 1 级托管堆中未被回收的对象将会升级至第 2 级:

这一代包含长生存期对象。 长生存期对象的一个示例是服务器应用程序中的一个包含在进程期间处于活动状态的静态数据的对象。

第 2 代托管堆中未被回收的对象会继续保留在第 2 代托管堆中,直到在将来的回收中确定它们无法访问为止。 大对象堆也是在这一代进行会输

活动对象

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

  • 堆栈根:由实时 (JIT) 编译器和堆栈查看器提供的堆栈变量。 JIT 优化可以延长或缩短报告给垃圾回收器的堆栈变量内的代码的区域。

  • 垃圾回收句柄:指向托管对象且可由用户代码或公共语言运行时分配的句柄。

静态数据:应用程序域中可能引用其他对象的静态对象。 每个应用程序域都会跟踪其静态对象

   在垃圾回收启动之前,除了触发垃圾回收的线程以外的所有托管线程均会挂起。所以频繁的垃圾回收会造成性能的损失。


http://www.kler.cn/a/320614.html

相关文章:

  • WebAssembly在桌面级应用开发中的探索与实践
  • 华为USG5500防火墙配置NAT
  • 如何在 Ubuntu 上安装 Emby 媒体服务器
  • C++网络编程之SSL/TLS加密通信
  • 【计算机网络】协议定制
  • 【目标检测】【Ultralytics-YOLO系列】Windows11下YOLOV5人脸目标检测
  • 阿里云服务器发布node服务后,连接不上
  • 简单分享下Python的if
  • 华为HarmonyOS灵活高效的消息推送服务(Push Kit) - 4 获取Push Token
  • 科研绘图系列:R语言连线点图(linechart dotplot)
  • 浅析安科瑞Acrel-1000DP分布式光伏监控系统在某煤矿5MW分布式光伏项目中的应用-安科瑞 蒋静
  • [mongodb][配置]MongoDB中限制内存
  • Ceph官方文档_02_Ceph初学者指南
  • 功能测试详解
  • npm run serve报错提示js堆内存不足
  • elastic search后端安装方法(服务端)
  • DCGAN生成人脸图片
  • g1:基于 Llama,用提示工程实现类似 o1 的深度推理
  • SpringBoot 与 Maven 快速上手指南
  • 使用Fiddler Classic抓包工具批量下载音频资料
  • 从HarmonyOS Next导出手机照片
  • 使用python-pptx批量删除备注:清除PPT文档中的所有备注信息
  • 开源集成开发环境搭建之VSCode启动Jupyter Notebook
  • 科技赋能:智慧厕所,实现公共厕所精细化管理@卓振思众
  • SadTalker模型部署教程
  • OceanBase 3.X 高可用 (一)