zoukankan      html  css  js  c++  java
  • [.NET] GC垃圾回收机制

    前言:

    在.NET程序开发中,为了将开发人员从繁琐的内存管理中解脱出来,将更多的精力花费在业务逻辑上,CLR提供了自动执行垃圾回收的机制来进行内存管理。开发人员甚至感觉不到这一过程的存在。CLR执行垃圾回收的过程,有以下几点:

    • 如何判断哪些对象是可以进行回收的,哪些是要保留的?
    • 对象在堆上是如何分布的?何时执行垃圾回收?
    • 垃圾回收的过程如何进行的?有哪些优化策略?

    判断哪些对象需要进行回收

    垃圾回收,第一步要判断哪些对象需要被GC回收掉。简而言之,那些在代码的任何位置也无法访问到的对象是需要被GC回收掉的。

    GC借助于应用程序根(Application Roots)和对象图(Object Graph)来判断哪些对象需要被回收。应用程序根保存了堆上对象的引用,如果一个对象没有直接或者间接的被应用程序根所引用,那么就说明没有任何代码可以访问到它,因此这个对象可以被回收。

    应用程序根只是一个入口点,一个对象可能持有其他一个或者多个对象的引用,这种对象间的引用关系构成了对象图。每次创建新对象,在复制引用、删除引用,或者执行垃圾回收之后,CLR都会自动更新它。

    对象如何分配在堆上

    .NET中的对象创建在托管堆上,托管堆维护着一个指针,这个指针标识了下一个将要创建的新对象所在位置,通常称作新对象指针。该指针总是位于托管堆的末尾。当使用new运算符创建对象时,CLR将会执行下面几个主要操作:

    • 计算此对象所要分配的内存大小;
    • 检查托管堆,确保托管堆有足够的空间来建立这个对象。如果空间够,将会调用构造函数来创建对象,并在托管堆末尾创建对象,然后返回对象在托管堆上的引用。引用的地址即为新对象指针所指向的地址。如果空间不够,则会执行一次垃圾回收来释放空间;
    • 修改新对象指针的值,使它指向上一个可用的地址。

    垃圾回收的执行过程

    GC有许多算法,常见的算法有Reference Counting, Mark Sweep, Copy Collection等,目前主流的虚拟系统.NET CLR, Java VM都采用的Mark Sweep算法。Mark-Sweep标记清除阶段:先假设Managed Heap中所有对象都可以被回收,然后找出不能回收的对象,给这些对象打上标记,最后Managed Heap中没有打标记的对象都可以被回收。Compact压缩阶段:对象回收之后Managed Heap内存空间变得不连续,在Managed Heap中移动这些对象,使他们重新从Managed Heap基地址开始连续排列。

    执行垃圾回收的时机:

    • 当通过new关键字创建对象时,如果发现托管堆所占用的内存已经达到一个临界点,就会进行垃圾回收;
    • 应用程序退出时也会执行一次垃圾回收;
    • 调用System.GC.Collection()方法强制执行;

    垃圾回收过程中的优化策略

    垃圾回收过程中还采用了一些优化策略,主要时对象代(Object Generation)和大对象堆(Large Object Heap)

    1. 对象代

    对象共分了三个代级:

    • 第0代:新近分配在托管堆上的对象,从来没有被GC回收过,任何一个新对象,当它第一次被分配在托管堆上时,就是第0代;
    • 第1代:经历过一次垃圾回收后,依然保留在堆上的对象;
    • 第2代:经历过两次或者以上垃圾回收后,依然保留在堆上的对象。

    当进行垃圾回收时,垃圾回收器将会首先检查所有的第0代对象,并对其中可回收的对象进行清理。如果清理后获得足够的空间,经历过垃圾回收后的对象将提升为第1代对象。

    如果所有的第0代对象都检查过了,但是内存空间还不够,那么将会检查第1代对象的可访问性,并进行垃圾回收。此时,如果经历过垃圾回收的第1代对象仍保留在堆上,则会升级为第2代对象。类似的,如果内存仍不够用,将会对第2代对象进行检查和垃圾回收。如果第2代的部分对象在此次垃圾回收后仍然保留在堆栈上,它依然是第2代对象。因为总共定义了3代对象。如果第2代对象在进行完垃圾回收后空间依然不够用,则会抛出OutOfMemoryException异常。

    可见,最容易清理掉的就是那些新对象。

    2. 大对象堆

    垃圾回收的过程中有一个很影响性能的地方,就是在压缩的过程中,因为要批量地挪动对象,以填充腾出来的空间,如果对象很大,那么要移动的数据量就会很大。

    如果将大对象直接分配在第0代,那么第0代的空间很快就会被占满,从而迫使CLR执行一次垃圾回收,这样执行垃圾回收的次数就会变得很频繁。

    因此,第二个优化策略就是采用大对象堆,当对象大小超过85kb时,就会被分配在大对象堆上。大对象堆有几个特点:

    • 没有代级的概念,所有对象都被视为第2代对象;
    • 不进行对象移动和空间压缩,因为移动大对象是相对耗时的操作。
    • 对象不会被分配在末尾,而会在链表中找合适的位置,因此会存在碎片问题。

    至此,就对.NET GC有了一个基本的了解。感谢您的阅读~

  • 相关阅读:
    sort和uniq去重操作【转】
    MySQL中interactive_timeout和wait_timeout的区别【转】
    Keepalived详解(五):Keepalived集群中MASTER和BACKUP角色选举策略【转】
    Keepalived详解(四):通过vrrp_script实现对集群资源的监控【转】
    Keepalived详解(三):Keepalived基础功能应用实例【转】
    Spring详解(六)------AspectJ 实现AOP
    深入理解计算机系统(1.3)------操作系统的抽象概念
    深入理解计算机系统(1.2)------存储设备
    深入理解计算机系统(1.1)------Hello World 是如何运行的
    深入理解计算机系统(序章)------谈程序员为什么要懂底层计算机结构
  • 原文地址:https://www.cnblogs.com/yang-fei/p/7521064.html
Copyright © 2011-2022 走看看