博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配
阅读量:6366 次
发布时间:2019-06-23

本文共 2540 字,大约阅读时间需要 8 分钟。

在" "中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配。我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收。

在" "中,我们了解了值类型参数和引用类型参数在传递时的内存分配情况。

在" "中,我们了解了在拷贝引用类型对象时的内存分配情况。

 

而本篇的重点要放在:对托管和非托管资源的垃圾回收、处理以及内存分配情况。

 

主要包括:

    □
    □
        ※
            ○
            ○
        ※
        ※
■        

 

  什么样的对象被GC认为是垃圾?

当托管堆中的对象不被任何其它对象所引用,这些对象将成为被释放的垃圾对象等待被GC回收。

 

每个应用程序都有一组根指针,这些根指针是不会被回收的,是由JIT编译器和CLR运行时维护的一个列表。主要包括:

● 全局/静态指针:指向全局或局部静态变量
● 栈指针:指向应用程序线程所需要的那部分栈上空间
● 寄存器指针:指向托管堆所需要的那部分CPU中的内存地址

 

以上,假设托管堆中有5个对象,1和5被跟指针引用,3依赖1,那么在这组托管堆对象中,2和4被回收后变成如下:

当运行时有新的引用对象产生,将会被放到托管堆中这组对象的最上面。

 

  GC如何回收?

  GC对托管堆中对象的回收

GC采用一定的算法在托管堆中遍历所有对象,最终形成一个可达对象和不可达对象,不可达对象将被回收。

 

  GC对非托管堆中对象的回收、处理

  对资源的回收

比如文件、数据库链接、网络链接等,这些不再托管堆中的对象如何被回收呢?

1、通过析构函数回收

public class Sample{    //析构函数    ~Sample() { } }

 

在托管堆中,那些带有析构函数的实例,将被放置到"Finalization Queue"中。

 

对于那些不被任何其它对象所引用,如果没有析构函数,比如2,将被直接回收,如果有析构函数,例如4,会被放到"Freachable Queue"中,等待GC实施下一轮回收。

当为一个类添加析构函数后,为GC增加了额外的工作,代价是比较昂贵的,更现实的做法是让类来实现IDisposable接口。

 

2、通过实现IDisposable接口回收

首先让一个类实现IDisposable接口。

public class ResourceClass : IDisposable{    public void Dispose() { //TODO:实现回收逻辑 } }

 

在应用程序中调用如下实施回收。

using(ResourceClass re = new ResourceClass()){    }

 

  对静态值类型变量的处理

class Counter{          private static int s_Number = 0; public static int GetNextNumber() { int newNumber = s_Number; // DO SOME STUFF newNumber += 1; s_Number = newNumber; return newNumber; } }

如上,当方法有方法处理静态字段就需要注意了,2个线程同时调用GetNextNumber()会得到相同的结果,而我们的本意是:每调用一次方法,静态字段s_Number自增1。

 

我们可以在处理逻辑块中加锁,每次只允许一个线程执行。

class Counter{          private static int s_Number = 0; public static int GetNextNumber() { lock (typeof(Counter)) { int newNumber = s_Number; // DO SOME STUFF newNumber += 1; s_Number = newNumber; return newNumber; } } }

 

  对静态引用类型变量的处理

class Olympics{          public static Collection
TryoutRunners;} class Runner { private string _fileName; private FileStream _fStream; public void GetStats() { FileInfo fInfo = new FileInfo(_fileName); _fStream = _fileName.OpenRead(); } }

以上,在GetStats()方法中,由于没有对FileStream及时关闭,如果Olympics恰巧有10万个Runner的集合,10万Runner都执行没有关闭FileStream的Gettats()方法,这将是一场灾难!

 

Singleton模式可以很好地避免上述问题,它保证了在任何时候,内存中只存在某个类的一个实例。

public class Earth{    private static Earth _instance = new Earth(); private Earth(){} public static Earth GetInstance(){ return _instance;} }

以上,单例模式的必要构成要素包括:

1、私有静态引用类型变量
2、私有构造函数
3、获取类实例的静态方法

 

  GC何时回收?

GC会周期性地执行垃圾回收、内存清理工作,以下情况会启动GC:

● 托管堆内存不足溢出时
● 调用GC.Collect()方法强制执行垃圾回收
● Windows报告内存不足
● CLR卸载AppDomain

 

  GC回收之后,又执行哪些操作?

GC在垃圾回收之后,托管堆上将出现多个被收集对象的"空洞",为了避免托管堆的内存碎片,会重新分配内存、压缩托管堆。

 

参考资料:

《你必须知道的.NET(第2版)》,作者王涛。

 

".NET的堆和栈"系列包括:

转载地址:http://ufrma.baihongyu.com/

你可能感兴趣的文章
使用sysbench压力测试MySQL(一)(r11笔记第3天)
查看>>
css知多少(11)——position
查看>>
【Spring】定时任务详解实例-@Scheduled
查看>>
先有的资源,能看的速度看,不能看的,抽时间看。说不定那天就真的打不开了(转)...
查看>>
[20161028]rman与filesperset=1.txt
查看>>
哪些领域适合开发微信小程序
查看>>
谁说数据库防火墙风险大?可能你还不知道应用关联防护
查看>>
ASP.NET Core应用针对静态文件请求的处理[2]: 条件请求与区间请求
查看>>
怎样做一个企业?尤其是在这个互联网时代
查看>>
DVNA:Node.js打造的开源攻防平台
查看>>
17个案例带你3分钟搞定Linux正则表达式
查看>>
Java 8 比较器:如何对 List 排序
查看>>
苹果是否步思科后尘折戟中国
查看>>
漏洞预警!微软曝光震网三代漏洞,隔离网面临重大危机
查看>>
协鑫集成第二批1000台E-KwBe光伏储能设备即将启运澳洲
查看>>
爱立信物联网广州路演
查看>>
云计算企业业绩分化明显 9家上市公司中期预喜
查看>>
《VMware Virtual SAN权威指南(原书第2版)》一3.5 可能发生的网络配置问题
查看>>
SK电讯发布Q2财报 净利润同比下降26.9%
查看>>
零售品牌如何驾驭大数据主导商业决策?
查看>>