余晖落尽暮晚霞,黄昏迟暮远山寻
本站
当前位置:网站首页 > 编程知识 > 正文

C# - 垃圾回收机制(*)值与引用,托管与非托管资源 088

xiyangw 2023-10-08 13:56 70 浏览 0 评论

在.NET Framework 中,内存中的资源(即所有二进制信息的集合)分为“托管资源”和“非托管资源

C#中数据分为值类型与引用类型;

值类型:数据存储在栈中;属于非托管资源,不需要通过垃圾回收机制管理

引用类型存储在栈与堆中(对象的引用存储在栈中,数据存储在堆中)其中堆中的数据属于托管资源,需要由垃圾回收机制进行管理(相当于由"系统自动管理内存")

在.Net的CLR(通用语言运行时)中有一个核心功能即垃圾回收机制(简称GC),其作用就是回收托管堆中的内存资源,一般由系统自动调用(也可以手工调用,但不建议这么做)可以回收C#中的引用类型在托管堆中的资源(所占内存),值类型不用管(这个不需要GC,会自动释放)

“非托管资源”:需要由程序员手工调用;如文件句柄,数据库连接,网络端口等资源

"垃圾回收”机制是 .NET Framework 的特性,而不是C#的,需要注意的是:

1)值类型(包括引用和对象实例)和引用类型的引用不需要通过GC来释放内存,因为当它们出 了作用域后会自动释放所占内存

2)GC只对引用类型的引用所指向的对象实例保存的托管堆资源起作用

3)GC并不是实时进行回收(不是没有没有对象引用立马进行回收)

垃圾回收的目的:1)使程序员可以将精力集中在实际的问题上而不用分心来管理内存的问题 2)大大减少了内存人为管理不当所带来的Bug 3)最终目的提高内存利用率

C#中垃圾指的是什么:引用类型中没有变量(栈中的变量)引用的对象(堆中的对象)表示可以GC进行回收的连接(不是立即进行回收)

垃圾回收的机制:GC中使用了mark-and-compact(标记和压缩)算法,在GC中有"代"的概念(下面细说)对可以暂时存活的对象进行标记,没有进行标记的会被GC回收:压缩:是将被GC回收后留出的不连续的空间,移动到一起(形成连续的空间)类似电脑的碎片整理

//定义Person类,类中有一个Age属性
示例1:没有变量引用的对象,表示可以被回收的"垃圾"
Person p1 = new Person();
p1.Age = 18;
Person p2 = new Person();
p1 = p2;当执行了这句话
p1指向的对象(堆中的对象),已经没有变量指向了
因此p1就变成了可以被回收的"垃圾"
相当于 Person p1 = null;
//============================
示例2:即使设置了null,依然不能被回收
Person p3 = new Person() { Age = 18 };//对象初始化器
Person p4 = new Person() { Age = 20 };
Person[] ps = new Person[] { p1, p2 };
p1 = null;//即使没有引用的对象
//但是还有一个ps[0]引用着p1对象,所以还是不能被回收
Console.WriteLine(ps[0].Age);

//进行手动进行回收
GC.Collect();//对所有"代"进行强制回收
//最好不要进行手动调用使其进行强制回收,
//因为垃圾回收机制自己有一系列的算法策略,需要移动对象等等
//进行强制回收,为了达到目的,需要暂停应用程序的已处理
//如果频繁的调用垃圾回收会影响整个程序的性能
Console.ReadKey();

示例,释解图

.NET将heap(用于分配管理)分成3个代龄区域:Gen 0,Gen 1,Gen 2,按照对象的生命周期,将对象分为新旧两种情况,对不同的情况使用不同的回收算法与策略,加强对新区域的回收处理力度,争取在较短的时间间隔,较小的内存区域内,以较低成本将执行路径上大量新近抛弃不再使用的局部对象及时回收掉,释放占有的内存

分代的益处:避免每次回收遍历所以的对象,减少逻辑回收时间,提高工作效率

GC有3种方式:#Gen 0 collections,#Gen 1 collections,#Gen 2 collections;

如果Gen 0 heap内存达到阀值,则触发0代GC,0代GC后Gen 0中幸存的对象进入Gen1;

如果Gen 1的内存达到阀值,则进行1代GC,1代GC将Gen 0 heap和Gen 1 heap一起进行回收,幸存的对象进入Gen2

  Gen2的GC将Gen 0 heap,Gen 1 heap和Gen 2 heap一起回收,Gen 0和Gen 1比较小,这两个代龄加起来总是保持在16M左右;Gen2的大小由应用程序确定,可能达到几G,因此0代和1代GC的成本非常低,2代GC称为full GC,通常成本很高;粗略的计算0代和1代GC应当能在几毫秒到几十毫秒之间完成,Gen 2 heap比较大时,full GC可能需要花费几秒时间,因此大致上来讲.NET应用运行期间,2代,1代和0代调用GC的频率大致为1:10:100

GC内部示意图

0代假设设置了200KB内存,只能存储5个对象,当0代中储存的对象达到阈值(设置的临界值)GC会自动将没有引用的对象进行回收(如p2,p4),0代中的幸存者(p1,p3,p5)会被移动到1代中,剩下的会进行"压缩处理"即p1nullp3null,p5一>p1,p3,p5,null,null,再有新的对象存放在后两个null中,下面两代中都会经历类似的"压缩处理",将断续的空间整理为连续的空间

当1代中储存的对象也达到阈值(假设是2MB内存),GC会自动将没有引用的对象进行回收(如p2,p4),0代中的幸存者(p1,p3,p5)会被移动到1代中;其中p11-p13是0代又达到阈值被GC清理过的幸存者,当1代中的对象即使经过GC清理还是达到了阈值,就会将1代中的幸存者移动到第2代

当2代中储存的对象达到了阈值(假设是2GB内存)假设又有要储存新的对象(假设1MB)而GC无论怎么回收0,1,2三代中的"垃圾"内存中都不没有1MB的空间就会报异常(类似Out of Memory内存不够用)

注意:0代不一定非到阈值才会回收(不定时回收)1,2代是达到阈值才会回收;优先回收第0代,没有回收的移动到第1代,1代没有回收的移动到2代,都满了2代会试着扩展,如果内存不够扩展所需就报异常

程序中除了被托管的内存资源由GC处理,其他非托管资源可以通过~析构(或终结)方法或实现接口IDisposableDispose()方法释放内存(再比如使用using,之后会举例说明)

 class Class1 : IDisposable
 {
     #region IDisposable 成员
     public void Dispose()
     {
         //这里的代码,来释放除内存资源外的其他资源
     }
     #endregion
     //通过反编译工具查看
     //在c#中叫Finalize()函数 中文:终结函数(析构方法)
     //在当前对象被垃圾回收之前会调用Finalize()函数,释放其他资源(如数据库连接等).
     ~Class1()
     {
         //无法手动调用,一般用于回收其他内存,都写在Dispose()方法中
         //既然Dispose()方法已经释放其他内存,这时,就不再调用Finalize()方法了,
         //所以就通过 GC.SuppressFinalize(this);告诉程序不调用Finalize()了
         
          //通过查看Stream类的Dispose()
         //GC.SuppressFinalize(this)方法位于Close()方法中
         //Close()方法位于Dispose()方法中
     }
 }

相关推荐

排序算法--归并排序_归并排序例题讲解

原理如图所示(先分割再合并):归并排序代码工作原理:1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列2、设定两个指针,最初位置分别为两个已经排序序列的起始位置3、比较两个指针所...

八大排序算法-归并排序_归并排序 算法

算法思想归并排序分为三个步骤:1.分解:将数列分解成n个子数列。(如果是将数列分成2个子数列则为2路归并)2.治理:对每个子数列进行排序操作3.合并:将两个排好序的子数列进行合并生成新的数列算法实现P...

高级排序之归并排序、希尔排序_希尔排序和归并排序区别

前言继上次排序算法简单排序算法之冒泡、插入和选择排序-Java实现版后,本文学习高级排序算法——归并排序、希尔排序,快速排序将在后续更新。本文实现代码调用方法,部分来自前一个文章:简单排序算法之冒泡、...

Excel办公应用:按合并单元格排序的三大方法

1.按姓名对科目排序重点:在"C2"中输入公式=IF(A2<>"",1,C1+1),然后下拉填充。2.按姓名添加连续序号(方法一)重点:选择"A2:A11"单元格区域,在编辑栏中输入公...

快速排序 Vs. 归并排序 Vs. 堆排序——谁才是最强的排序算法

知乎上有一个问题是这样的:堆排序是渐进最优的比较排序算法,达到了O(nlgn)这一下界,而快排有一定的可能性会产生最坏划分,时间复杂度可能为O(n^2),那为什么快排在实际使用中通常优于堆排序?昨天刚...

归并排序思路图解 #归并排序_归并排序百度百科

排序算法1.图解。OK,让它排一下。看好了,要开始排了。能看出来像递归吗?肯定算法难,但是这个次数非常的多,不用管次数。这个是帝规,就是递归。这是并,这是并,这是两个有序数,组合成一个最后的大的有序数...

排序算法学习——归并排序_归并排序算法稳定吗

我们先看归并排序的定义归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(DivideandConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每...

动画|经典的归并排序究竟怎么玩儿?

作者|菠了个菜责编|郭芮由于LeetCode上的算法题很多涉及到一些基础的数据结构,为了更好的理解后续更新的一些复杂题目的动画,推出一个新系列——《图解数据结构》,主要使用动画来描述常见的数据...

Excel中,多列数据统一排名,Rank函数直接搞定

Rank实现多列联合排序排序,那太简单啦,Excel中,升序降序,一个按键就可以。但,那是针对单列情况,若需要联合多列数据进行排序呢?如下图所示,需要对1、3、5列进行统一排序,咋弄嘞?联合排序案例先...

【数据结构与算法】归并排序_数据结构中归并排序

归并排序是建立在归并操作的一种高效的排序方法,该方法采用了分治的思想,比较适用于处理较大规模的数据,但比较耗内存,今天我们聊聊归并排序排序思想一天,小一尘和慧能坐在石头上,眺望着远方师傅,我听山下的柳...

C++基础算法:归并排序_经典排序算法-----归并排序(c语言实现)

归并排序(MergeSort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(DivideandConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列。...

马士兵说之归并排序_马士兵教育的内推是真的

大家对于排序应该是挺熟悉的吧,马士兵老师特意为排序出了一波视频,当然文章是转自博客园的,马士兵老师的视频观看请点击下方的了解更多概要本章介绍排序算法中的归并排序。内容包括:1.归并排序介绍2.归并...

C++快速排序和归并排序_c++快速排序sort

快速排序每一轮挑选一个基准元素(随机选择,编程时一般选取第一个),并让比它大或小的元素移动到基准元素的两边,把数列拆解成了两个部分。而后对这两部分分别进行快速排序。时间复杂度:O(nlogn),辅助空...

经典的排序算法——归并排序_归并排序算法步骤

归并排序(MergeSort)是一种基于分治策略的高效排序算法。它将原始数组不断地分割成两个子数组,直到每个子数组只剩下一个元素为止(即基本有序),然后再通过合并已排序的子数组来最终得到完全有序的大...

归并排序_归并排序c++实现

归并排序概念:归并排序中涉及到一个概念就是分而治之,总序列化成小序列,将小序列排序好,利用排序好的小序列,再归并排序成原来要排序的序列。所以排序前先要分:functiondivide(arr){...

取消回复欢迎 发表评论: