Garbage collection in .NET

table of Contents

l          Introduction

l          About Garbage Collection

l          garbage collection algorithm

m         application root (Application Roots)

l          realize

m         Phase I: mark (Mark)

m         Phase II: finishing (Compact)

l          End (Finalization)

l          optimize performance recovered waste

m         weak reference (WeakReference)

m        代(Generations)

l          garbage collection-related myths

 

preface

       Microsoft claims that .NET is a revolutionary programming techniques. Many factors make it the first choice for most developers. In this article we will discuss about the .NET Framework in a very major advantage - memory and resource management convenience.

 

About garbage collector

       Each program will use a certain order of resources, or memory buffer, or network connection, or database resources, and so on. In fact, in an object-oriented environment, each type is seen to be some effective resource programs. In order to use these resources must be allocated to describe this part of the memory type.

 

       Resource access performed by clicking the steps:

 

1. To describe the type of memory to allocate resources.

2. Initialization resource, the resource set to the initialization state, the resource is available.

3. Resource use by members of the access type instance (repeat request).

4. Destruction of state resources to clear the resources.

5. Free memory.

 

The .NET garbage collector (GC) to help developers fully and completely from memory usage tracking and determine when to release within freed.

 

Microsoft® .NET CLR (Common Language Runtime) requires that all resources are allocated from the managed heap. You do not need to release the object on the managed heap - when the application is no longer needed these objects, the object is automatically released.

 

Memory is not unlimited. Recycling garbage collector needs to perform in order to free up memory. Optimization engine will do the garbage collector has been assigned to choose the best recovery time (precise criteria provided by Microsoft). When the garbage collector to perform the recovery, first find out the object on the managed heap that are no longer used by the application and then perform the appropriate action to recover the memory of these objects.

 

       However, in order for automatic memory management, GC must know the root of the seat. That is, it should know what an object when the application is no longer used. In .NET, GC by what is called a metadata aware of these. .NET each data type are used to describe it through metadata. With the help of metadata, CLR know the memory layout of each object, providing help to the GC at the finishing stage of garbage collection. Without this information, GC will not know where an object ends and the next begins where.

 

Garbage collection algorithm

Application root (Application Roots)

       Each application has a root (Roots). Root identifier storage location, this location to an object or a managed heap, objects or points to an empty (null).

 

such as:

 

l application of all global and static object pointers.

l a thread stack all local variables / parameters object pointer.

l CPU register all managed heap object pointer.

L FReachable object pointer queue.

 

Table roots activity is maintained by the JIT compiler and the CLR, and for the garbage collector algorithm is accessed.

 

achieve

Garbage collection in .NET is tracking the recovery realized, precisely CLR implements mark (Mark) / Finishing (Copact) collector.

 

This method has the following two phases:

 

Phase I: mark (Mark)

Can be found on recovered memory.

 

When the GC starts running, it is assumed that all objects in the heap of rubbish. In other words, it is assumed that the root of the application does not point to any object in the stack.

 

Phase I comprises the following steps:

 

1.  GC识别存活对象的引用或应用程序根。

2.  从根开始遍历,建一张可以从根遍历的所有对象的图。

3.  如果GC准备尝试添加一个已经在图中的对象,它就停止这条路径的遍历。这样做有两个目的,第一个是极大的优化性能,因为它不会遍历一套对象一次以上。第二是防止当有对象的循环连接列表时而发生死循环,因此循环被有效的控制了。

 

一旦所有的根都被检查完后,垃圾回收器的图中包含了所有可以从应用程序根遍历到的对象。任何不再图中的对象都不能被应用程序访问到,也就是所谓的垃圾。

 

阶段II:整理(Compact)

把所有存活的对象移到堆的末端,空出堆顶端的空间。

 

阶段II包含下列步骤:

 

1.  现在GC线性地遍历堆,寻找邻近的垃圾对象块(现在被认为是空闲空间)。

2.  然后GC往下移动内存中的非垃圾对象,去掉堆中的所有空隙。

3.  移动内存中的对象导致对象指针失效。因此GC需要修改应用程序的根使对象的指针指向新的位置。

4.  另外,如果对象包含一个指向其它对象的指针,GC也会负责纠正这些指针。

 

在所有垃圾被标识完以后,所有的非垃圾对象也被整理,并且所有非垃圾对象的指针也被修正,最后一个非垃圾对象后的指针指向下一个被添加对象的位置。

 

终结(Finalization)

 

.NET Framework的垃圾回收器能暗中追踪由应用程序创建的对象的生命周期,但是当它遇到对象包装了非托管资源(比如文件、窗口或网络连接等)时却无能为力。

 

一旦应用程序不再使用那些非托管资源时需要显示地释放它们。.NET Framework为对象提供了终结(Finalize)方法:在垃圾回收器收回这个对象的内存时,必须执行对象的这个方法来清除它的非托管资源。由于缺省的Finalize方法什么都没做,如果需要显示清除资源必须覆盖这个方法。

 

如果一把Finalize方法当作只是C++中析构函数另外一个名字那也不足为怪。虽然它们都被赋予了释放对象占有的资源的任务,但是它们还是有很不相同的语义。在C++中,当对象推出作用域时析构函数会立刻被调用,而Finalize方法是在起动垃圾回收清除对象时才被调用的。

 

.NET中,由于终结器(Finalizer)的存在使得垃圾回收的工作变得更加复杂了,因为它在释放对象前增加了许多额外的操作。

 

无论什么时候,在堆上分配一个含有Finalize方法的新对象时,都会有一个指向这个对象的指针被添加到一个称为Finalization队列的内部数据结构当中。当对象不能再次被遍历到时,GC就认为这个对象是垃圾。GC首先扫描Finalization队列查找这些对象的指针,当指针被找到时,把它从Finalization队列中去掉并添加到另外一个名为FReachable队列的内部数据结构中,使这个对象不再是垃圾的一部分。这时,GC完成了确定垃圾。然后整理(Compact)可收回的内存,由专门的线程负责清空FReachable队列并执行对象的Finalize方法。

 

第二次垃圾回收器被触发的时候,它把被终结(Finalize)的对象看作真正的垃圾,然后简单的释放它们的内存。

 

由此可知当一个对象需要终结时,它先死,然后存活(复活),然后再次并且最终地死去。推荐避免使用Finalize方法,除非有需要。Finalize方法会增加内存压力,因为直到两次垃圾回收被启动时,对象占用的内存和资源才会得到释放。因此你无法控制两次Finalize方法执行的顺序,它可能会导致无法预料的后果。

垃圾回收性能的优化

l         弱引用(WeakReference)

l         代(Generations)

 

弱引用(WeakReference)

       弱引用(WeakReference)是提高性能的一种方式,用于减少托管堆中大对象的压力。

      

       当一个根指向一个对象时,它被称为这个对象的一个强引用并且这个对象不能被回收,因为应用程序能遍历到这个对象。

 

       当一个对象有一个指向它的弱引用(WeakReference)时,基本上是指如果有内存请求并且GC启动时,这个对象可以被回收,当应用程序再次尝试去访问这个对象时,访问将会失败。另一方面,为了能访问一个被弱引用(WeakReference)的对象,应用程序必须获得一个对这个对象的强引用。如果应用程序在垃圾回收器回收这个对象之前获得了它的强引用,GC将不能回收这个对象,因为有这个对象的强引用存在。

 

       托管堆包含两个管理弱引用(WeakReference)的内部数据结构:短弱引用表和长弱引用表。

 

       两种类型的弱引用:

 

l         短弱引用不追踪复苏。

也就是说,一个有短弱引用的对象会被立即收回,而不用等到运行Finalize方法。

l         长弱引用追踪复苏。

也就是说,只有当长弱引用表中的对象的存储空间可收回的时候GC才回收这个对象。如果对象有Finalize方法,是在Finalize方法被调用了之后并且对象不能复活了。

 

       这两个表简单的存放着分配在托管堆中对象的指针。最初,两个表均为空。当你创建一个弱引用(WeakReference)对象时,对象不从托管堆中分配。而是在一个弱引用表中分配一个空的存储位置;短弱引用使用短弱引用表,长弱引用使用长弱引用表。

 

       让我们看一个例子,看看GC运行时会发生些什么。下面的图(图1和图2)显示了GC运行前和运行后所有内部数据结构的状态。

 

图1:GC运行前

 

 

图2:GC运行后

 

 

以下是GC运行时进行的操作:

 

1.  GC为所有可遍历的对象建一张图。在上面的例子中,图包含对象B,C,E,G。

2.  GC扫描短弱引用表。如果表中指针指向的对象不在图中,那么这个指针标识的是一个不可遍历的对象,短弱引用表中的这个位置置为null。在上面的例子中,对象D的位置置为null,因为它不是图的一部分。

3.  GC扫描Finalization队列。如果队列中的指针所指的对象不在图中,那么这个指针标识一个不可遍历的对象,指针从Finalization队列中移到FReachable队列中。这时,对象被认为是可遍历的,所以加到图中。在上面的例子中,对象A,D,F是不包含在图中但看作是可遍历的对象,因为它们属于Finalization队列。进而Finalization队列被清空。

4.  GC扫描长弱引用表。如果表中的指针指的对象不在图中(现在图包括FReachable队列中指针所指的对象),那么指针标识一个不可遍历的对象,所在位置置为null。由于对象C和F都包含在图中,都不置null。

5.  GC整理(Compact)内存,挤出不可遍历对象留下的空隙。在上面的例子中,对象H是唯一从堆中删除的对象,它所分配的内存被收回。

 

代(Generations)

       由于垃圾回收要在停止整个程序的情况下才能完成,它们可能会在程序执行期间进行任意长时间的中断。GC也有可能中断为满足实时系统的需求而要求及时响应的事件。

 

       GC中有一个特征叫代(Generations),就是专门为提高性能而设计的。一个多代的GC是通过对观察用各种语言编写的大部分程序而得到两个事实进行仔细分析而得到的:

 

1.  新创建的对象拥有更短的生命周期。

2.  越老的对象,存活的越久。

 

多代的回收器通过对象的年龄把它分成若干组,并且年轻的对象比年老的对象回收的更频繁。初始化时,托管堆不含任何对象。所有新的对象都被添加到第0代堆中,直到堆装满了并触发垃圾回收。由于大部分对象存活的时间很短,只有一小部分年轻的对象在第一次回收时存活下来。一旦一个对象在第一次回收后存活下来后,它就被提升到第1代。在垃圾回收后可以说新的对象都在第0代堆中。只有当第0代的堆装满时垃圾回收才会再次被触发。所有第0代存活下来的对象被整理提升到第1代中。然后第0代不含任何对象了,但是所有新的对象都进入了第0代。

 

因此,作为当前代中“成熟”(存活于多代回收器)的对象,它们都会被移到下一级更老的代中。第2代是CLR的GC所支持的最大的代。以后回收时,第2代存活的对象将只是简单的停留在第2代。

 

因此,把堆划分成对象的代并且回收和整理更年轻的代中的对象提高了垃圾回收算法的效率,因为从堆中收回了大量的有意义的空间,同时比起回收器检查所有代中的所有对象要快得多。

 

一个能执行多代回收的GC,每次回收都要确保(至少要尽可能)所需时间小于某个最大时间,以帮助为实时环境能做一些配套的实时操作,同时也防止出现让用户明显感觉到的中断现象。

 

垃圾回收相关神话

GC显然比手工内存管理要慢

应解释:不是一定的。现代垃圾回收器看起来运行时和手工存储分配(malloc/free或new/delete)一样快。在一些特殊的程序中,垃圾回收运行的可能不如为用户专门设计的自定义内存分配那么快。但从另一方面说,为了使手工内存管理正确的工作而添加的额外代吗(比如说,显示的引用计数)常常比GC所做的要昂贵的多。

 

GC会使程序中断

对应解释:由于垃圾回收器在查找和回收垃圾对象时通常停止整个应用程序,他们可能会导致中断时间过长而让用户觉察到。但是通过现在优化计数,这些可以感觉到的中断完全可以避免。

 

手工内存管理不会导致中断

对应解释:手工内存管理并不能确保性能。它可能由于大量的分配或释放内存工作而导致中断。

 

使用GC的程序很大并且臃肿;GC不适合小的程序或系统

对应解释:尽管在复杂的系统中使用GC很有优势,也没有理由认为GC在其它尺寸的程序中会引入什么大的开销。

 

我已经听说了GC会使用两次大量的内存

对应解释:对于原来的GC这可能是个事实,但并不是垃圾回收器都是这样的。用于GC的数据结构比那些手工内存管理的要大的多。

转载于:https://www.cnblogs.com/zhangchenliang/p/4661824.html

Guess you like

Origin blog.csdn.net/weixin_34357962/article/details/93495547