内存管理与垃圾回收机制_垃圾回收:.Net中的内存管理

内存管理与垃圾回收机制

Garbage Collection: Memory Management in .Net

垃圾回收:.Net中的内存管理

During the days, when I was working with C++, as a basic rule for design and development, it was required to track all the memory usage and then carefully release all the memory acquired. Implementing proper resource management for our applications was always very difficult and a tedious task. It always distracted our concentration from the real problems that we were trying to solve. It was such a painful and disliked task that at times we used to wonder if we could have a mechanism that simplified this mind-numbing task of memory management.

在过去的日子里,当我使用C ++时,作为设计和开发的基本规则,需要跟踪所有内存使用情况,然后小心释放所有获得的内存。 为我们的应用程序实施适当的资源管理始终是非常困难且繁琐的任务。 它总是使我们的注意力从我们试图解决的实际问题上转移开。 这是一个痛苦而又不受欢迎的任务,有时我们常常想知道我们是否可以拥有一种简化这种内存管理难题的机制。

Then came the Garbage Collection in the Microsoft .NET common language runtime environment, which completely relived the developer from the task of all this memory management. However, the irony is the developers were still not happy.

然后是Microsoft .NET公共语言运行时环境中的垃圾回收,这使开发人员完全摆脱了所有这些内存管理的任务。 但是,具有讽刺意味的是,开发商仍然不满意。

Of all of the technologies to be found in .NET, the most controversial, seems to be garbage collection. This was because; the managed heap and the garbage collection mechanism, which are a key part of the .NET framework, have always appeared as foreign ideas to many of us. We often criticize the memory management and the garbage collector, as implemented by the .Net CLR, but the criticisms are typically based on a lack of understanding and nothing more.

在.NET中发现的所有技术中,最有争议的似乎是垃圾回收。 这是因为; 作为.NET框架的关键部分,托管堆和垃圾收集机制一直是我们许多人的陌生想法。 我们经常批评由.Net CLR实现的内存管理和垃圾收集器,但是批评通常是基于缺乏了解,仅此而已。

Let’s try and look at the mechanics of the Garbage Collection and the Memory Management. Let’s try and understand the steps of how the garbage collection algorithm works. I guess if we are able to explain how the resources are allocated and managed, then we would be in a better position to appreciate the importance and usefulness of the Garbage Collection.

让我们尝试看看垃圾回收和内存管理的机制。 让我们尝试了解垃圾收集算法如何工作的步骤。 我想如果我们能够解释如何分配和管理资源,那么我们将更容易体会到垃圾收集的重要性和实用性。

使用垃圾收集器进行内存管理 (Memory management using the Garbage Collector)

垃圾收集器是.Net CLR中的重要过程。 Microsoft .NET公共语言运行时要求从托管堆分配所有资源。 当应用程序不再需要对象时,它们将自动释放。

The CLR performs Non-deterministic finalization: i.e. it cannot be predicted, when the Garbage Collector will be called to clean up unnecessary objects from memory. Non deterministic finalization is performed when the CLR foresees a memory deficiency.

CLR执行非确定性终结处理:即,当调用垃圾收集器从内存中清除不必要的对象时,无法预测终结处理。 当CLR预见到内存不足时,将执行不确定的终结处理。

In all kinds of programs, resources are used. These resources could be in the form of ‘memory buffers’, ‘screen space’, ‘network connections’, ‘database resources', and so on. Moreover, we should realize that in an object-oriented environment, every type is actually a resource available for our program to use. To use any of these resources requires that memory be allocated to represent the type. The bare minimum, steps required to access a resource are as follows:

在各种程序中,都使用资源。 这些资源可以采用“内存缓冲区”,“屏幕空间”,“网络连接”,“数据库资源”等形式。 此外,我们应该认识到,在面向对象的环境中,每种类型实际上都是可供程序使用的资源。 要使用这些资源中的任何一种,都需要分配内存来表示类型。 访问资源所需的最低限度的步骤如下:

1. Allocate memory for the resource. (Resource Allocation – explained below)

1.为资源分配内存。 (资源分配–下文说明)

2. Initialize the memory to set the initial state of the resource.

2.初始化内存以设置资源的初始状态。

3. Use the resource by accessing the instance members of the type.

3.通过访问类型的实例成员来使用资源。

4. Reset the state of the resource for cleanup.

4.重置资源状态以进行清理。

5. Free the memory. (Garbage Collection – explained below)

5.释放内存。 (垃圾收集–下文说明)

资源分配 (Resource Allocation)

垃圾收集器分为两种数据类型:值类型和引用类型

1. Value types

1. 值类型

In C#, all the "things" declared with the following list of type declarations are Value types (because they are from System.ValueType):

在C#中,使用以下类型声明列表声明的所有“事物”均为值类型(因为它们来自System.ValueType):

• bool                                            • byte                                            • char

•bool•字节•char

• decimal                                      • double                                        • enum

•十进制•双精度•枚举

• float                                            • int                                               • long

•浮点数•整数•长

• sbyte                                          • short                                          • struct

•sbyte•短•struct

• uint                                             • ulong                                          • ushort

•uint•乌龙•ushort

These Value Types reside on the stack. The CLR does not care about this type of data with reference to memory management.

这些值类型驻留在堆栈上。 对于内存管理,CLR并不关心此类数据。

The Stack is self-maintaining, meaning that it basically takes care of its own memory management.  When the top box is no longer used, it's thrown out.

堆栈是可自我维护的,这意味着它基本上负责自己的内存管理。 如果不再使用顶部框,则将其丢弃。

2. Reference Types

2. 参考类型

All the "things" declared with the types in this list are Reference types. They inherit from System.Object

用此列表中的类型声明的所有“事物”都是引用类型。 他们从System.Object继承

• class                                             • interface                                             • delegate

•类•接口•委托

• object                                            • string

•对象•字符串

These Reference Types exist on a managed heap. This is where the Garbage Collector comes into the picture.

这些引用类型存在于托管堆上。 这是垃圾收集器进入图片的地方。

托管堆 (Managed Heap)

托管堆是一个内存池,在其中创建引用类型的所有实例(类和数组等)并分配空间。

When an object is initialized, the CLR simply allocates an address space in the Managed Heap. There is actually no storage space allocated at this point of time. However a pointer to this heap is maintained to keep track of the next object within this heap. The memory space on this heap is allocated only when an object is created using the new operator.

初始化对象时,CLR只是在托管堆中分配地址空间。 在此时间点实际上没有分配存储空间。 但是,将维护指向此堆的指针,以跟踪该堆中的下一个对象。 仅当使用new运算符创建对象时,才分配此堆上的内存空间。

Managed Heap

Mechanics of Resource Allocation - Object creation, initialization and instantiation

资源分配机制-对象创建,初始化和实例化

Initially, the next object pointer is set to the base address of the reserved address space region.

最初,下一个对象指针被设置为保留地址空间区域的基地址。

Empty Heap

After declaring an object, we instantiate the object using the 'new' operator. This ‘new’ operator first makes sure that the bytes required by the new object fit in the reserved region (committing storage if necessary). If the object fits, then pointer points to the object in the heap, this object's constructor is called, and the new operator returns the address of the object. At this point, ‘Next Object Pointer’ is incremented past the object so that it points to where the next object will be placed in the heap.

声明对象后,我们使用“ new”运算符实例化该对象。 该“新”运算符首先确保新对象所需的字节适合保留区域(必要时进行存储)。 如果对象合适,则指针指向堆中的对象,该对象的构造函数被调用,并且new运算符返回对象的地址。 此时,“下一个对象指针”将递增经过对象,以便指向下一个对象将在堆中放置的位置。

Adding Objects to Heap

In this process, it might reach to a condition where there is no more space left in the heap to be allocated to a new object, or the available space is not enough to accommodate the new object that has been instantiated. This is to say, when the ‘Next Object Pointer’ reaches beyond the end of the address space, a memory deficiency occurs and therefore the CLR calls the Garbage Collector and gives it this memory heap.

在此过程中,它可能会达到这样的状态:堆中没有剩余空间可分配给新对象,或者可用空间不足以容纳已实例化的新对象。 这就是说,当“下一个对象指针”到达地址空间的末尾时,就会出现内存不足的情况,因此CLR会调用垃圾回收器并为其提供此内存堆。

Heap Short of Space

Application Roots

应用根

Before we proceed to examine and understand the Garbage Collection process, let’s take a final look at the Managed Heap.

在继续检查和理解垃圾收集过程之前,让我们最后看一下托管堆。

So, we see that the required memory space is allocated on the heap, to all the objects that have been initialized. An Object Pointer is set to the final location and stores the address, where the next object would be allocated the memory space on the heap. Is this all? No it is not.

因此,我们看到所需的内存空间在堆上分配给了所有已初始化的对象。 对象指针设置为最终位置并存储地址,在该地址下一个对象将在堆上分配到内存空间。 这是全部吗? 不它不是。

Many objects on a heap are linked one to the other forming a chain, depending on which object is referenced by whom. There might be multiple such chains in the heap. The reference to the first object, i.e. the memory location to any such chain is called a Root. Every application has a set of such roots. E.g. the global and static object pointers, any local variable/parameter object pointers and any CPU registers containing pointers to objects.

堆上的许多对象彼此链接在一起,形成一个链,具体取决于哪个对象引用了谁。 堆中可能有多个这样的链。 对第一个对象的引用,即对任何此类链的存储位置,称为根。 每个应用程序都有一组这样的根源。 例如,全局和静态对象指针,任何局部变量/参数对象指针以及任何包含指向对象指针的CPU寄存器。

All these roots are stored in the Application stack and identify storage locations, which refer to objects on the managed heap or to objects that are set to null. This list of active roots is maintained by the just-in-time (JIT) compiler and common language runtime, and is made accessible to the garbage collector's algorithm.

所有这些根都存储在应用程序堆栈中,并标识存储位置,这些位置引用托管堆上的对象或设置为null的对象。 此活动根列表由即时(JIT)编译器和公共语言运行时维护,并且使垃圾回收器的算法可以访问。

Application Roots

垃圾收集器 (The Garbage Collector)

垃圾收集器使用的基本算法非常简单:

• Mark all managed memory as garbage

•将所有托管内存标记为垃圾

• Look for used memory blocks, and mark them as valid

•查找已使用的内存块,并将其标记为有效

• Discard all unused memory blocks

•丢弃所有未使用的内存块

• Compact the heap

•压缩堆

Once the GC receives this memory heap, The garbage collector checks to see if there are any objects in the heap that are no longer being used by the application. If such objects exist, then the memory used by these objects can be reclaimed. (If no more memory is available for the heap, then the new operator throws an OutOfMemoryException.)

GC收到此内存堆后,垃圾收集器将检查堆中是否有不再由应用程序使用的对象。 如果存在此类对象,则可以回收这些对象使用的内存。 (如果没有更多的内存可用于堆,则new运算符将引发OutOfMemoryException。)

Now there is a very valid question. How does the garbage collector know if the application is using an object or not?

现在有一个非常有效的问题。 垃圾收集器如何知道应用程序是否使用对象?

When the garbage collector starts running, it makes the assumption that all objects in the heap are garbage. In other words, it assumes that none of the application's roots refer to any objects in the heap. Now, the garbage collector starts checking the roots and building a graph of all objects reachable from the roots.

当垃圾收集器开始运行时,它假定堆中的所有对象都是垃圾。 换句话说,它假定应用程序的任何根均未引用堆中的任何对象。 现在,垃圾收集器开始检查根并为从根可到达的所有对象建立图形。

Managed Heap before Garbage Collection

Here it is important to note that, if while traversing down a chain, the garbage collector reaches an object that it has already added to the Graph, then it stops going further down that path. This is because, it is obvious that the next objects further down the chain would already be there in the Graph. This increases the performance of the process as well as it also prevents infinite loops. In case there are any circular linked lists of objects.

在这里重要的是要注意,如果在遍历一条链时,垃圾收集器到达了一个已经添加到Graph的对象,那么它将不再沿着该路径继续前进。 这是因为,很明显,图链中已经存在下一个对象。 这不仅可以提高过程的性能,还可以防止无限循环。 如果有任何循环链接的对象列表。

Once all the roots/chains have been checked and added to the Graph, the Garbage Collector assumes that are not in the Graph are not referenced from the application's roots and hence are not accessible by the application, and are therefore considered garbage. The garbage collector now checks the entire heap for contiguous blocks of garbage objects. It shifts the non-garbage objects down in the heap, removing all of the gaps in the heap. It then modifies the application's roots so that all the pointers now point to the objects' new locations. Also, if any object contains a pointer to another object, it corrects these pointers as well. Finally, the Next Object Pointer is positioned just after the last non-garbage object.

一旦检查完所有的根/链并将其添加到图上,垃圾收集器就会假定未从应用程序的根中引用图中未包含的图,因此应用程序无法访问它们,因此被视为垃圾。 现在,垃圾收集器将检查整个堆中是否存在垃圾对象的连续块。 它将非垃圾对象在堆中向下移动,从而消除了堆中的所有间隙。 然后,它修改应用程序的根,以便所有指针现在都指向对象的新位置。 同样,如果任何对象包含指向另一个对象的指针,它也会更正这些指针。 最后,下一个对象指针位于最后一个非垃圾对象之后。

Now the heap is ready to allocate the memory space, required by the new object that was initialized and had triggered the Garbage Collection.

现在,堆已准备好分配内存空间,这是初始化并触发垃圾回收的新对象所需的。

Managed Heap after Garbage Collection

世代 (Generations)

尽管垃圾收集器的此功能解决了即使在垃圾收集之后,堆上仍然没有足够的空间容纳新对象的问题。 但是,那么说Generations功能仅针对并解决了内存容纳问题是绝对错误的。 实际上,垃圾回收器的此功能纯粹是为了提高性能而存在。

The Garbage Collector with this feature is known as an ‘Ephemeral Garbage Collector’, and it makes the following assumptions:

具有此功能的垃圾收集器称为“临时垃圾收集器”,它进行以下假设:

•  The newer an object is, the shorter its lifetime will be.

•对象越新,其寿命将越短。

•  The older an object is, the longer its lifetime will be.

•对象越旧,其寿命将越长。

•  Newer objects tend to have strong relationships to each other and are frequently accessed around the same time.

•较新的对象之间往往具有很强的关系,并且经常在同一时间访问。

•  Compacting a portion of the heap is faster than compacting the whole heap.

•压缩一部分堆比压缩整个堆要快。

In order to understand, physically the Generations are nothing more than the identification of the Heap area into 3 categories. Once the Garbage Collector receives the memory heap, it divides/distributes the contents in the heap into three categories of objects and stores these objects into three tables:

为了理解,从物理上讲,世代无非就是将堆区域分为3类。 垃圾收集器接收到内存堆后,就会将堆中的内容划分/分配为三类对象,并将这些对象存储到三个表中:

•  Generation 0 this stores short lived objects.

•第0代可存储短暂的对象。

•  Generation 1 this stores older objects.

•第1代存储较旧的对象。

•  Generation 2 this stores the oldest objects.

•第2代将存储最旧的对象。

Generation 0第0代

At first when there are no objects on the Heap, then every time a new object is added, it goes into Generation 0. , as discussed above. Hence the objects in the Generation 0 are all latest objects that have never been examined by the Garbage Collector.

首先,当堆上没有任何对象时,然后每次添加新对象时,它都会进入Generation0。如上所述。 因此,第0代中的对象都是垃圾收集器从未检查过的所有最新对象。

Managed Heap - Generation 0
Generation 1第一代

Here Objects A, C, E, F and I are free objects, i.e. they do not have any reference, neither from a Root nor from any other object on the heap.  When this Generation 0 is completely full, i.e. there is no more space for a new Object then the garbage collection occurs. All the objects that survive the collection (Objects B, D, G and H) are compacted into the left most portion of the heap. These objects are the old objects and are now said to be in Generation1.

这里的对象A,C,E,F和I是自由对象,即它们既没有来自Root的引用,也没有来自堆上其他任何对象的引用。 当第0代完全填满时,即没有更多空间可容纳新对象,则发生垃圾回收。 将所有幸存的对象(对象B,D,G和H)压缩到堆的最左侧。 这些对象是旧对象,现在称为第一代。

Managed Heap - Generation 1
Generation 2第二代

Same as before, if again the Generation 0 is full, then again the heap is subjected to Garbage Collection. However, this time first all the objects in Generation 1, that survive the collection (Objects B, G & H) are compacted and are now considered to be in Generation 2.

与之前相同,如果第0代已满,则再次对堆进行垃圾回收。 但是,这一次首先压缩了第1代中所有幸存的对象(对象B,G和H),现在将其视为第2代。

Then, all the objects in Generation 0 are compacted as before and are now considered to be in Generation 1. This clears the space in Generation 0 for new objects.

然后,像以前一样压缩第0代中的所有对象,现在将其视为第1代。这将清除第0代中新对象的空间。

Hence, we see that in Generation 2 we have the oldest objects (objects B, G & H), in generation we have relatively older objects (objects K & N) and in generation we have the most recently added i.e. the newest objects (objects O, P, Q & R).

因此,我们看到在第二代中,我们拥有最旧的对象(对象B,G和H),在一代中,我们拥有相对较旧的对象(对象K和N),而在一代中,我们拥有最新添加的对象(即最新对象)(对象O,P,Q和R)。

Managed Heap - Generation 2

But now the question is, how exactly does this feature of generations, help in improving the performance?

但是现在的问题是,世代相传的功能究竟如何帮助改善性能?

1)  When the heap fills and a collection occurs, the garbage collector first examines and collects the objects in the Generation 0. The assumption is that the newer an object is, the shorter its lifetime is expected to be. Hence, we hope that, collecting and compacting the Generation 0 objects would free enough space on the heap to accommodate the new object. We can see that, examining and collecting objects from just the Generation 0 is much faster than if the collector had examined the objects in all the generations.

1)当堆填满并发生收集时,垃圾收集器首先检查并收集第0代中的对象。假设是,对象越新,其生存期就越短。 因此,我们希望收集并压缩第0代对象可以在堆上释放足够的空间来容纳新对象。 我们可以看到,仅检查和收集第0代的对象比收集器检查所有各代的对象要快得多。

However, even if collecting objects from Generation 0 doesn't provide the required amount of space, then the collector attempts to collect the objects from generations 1 and 0.

但是,即使从第0代收集对象没有提供所需的空间量,收集器也会尝试从第1代和第0代收集对象。

Similarly, if the collection from both the generations 1 & 0, fails to create the required space, then the objects are collected from all the Generations-2, 1, and 0.

同样,如果来自第1代和第0代的集合都无法创建所需的空间,则将从第2代,第1代和第0代的所有对象中收集对象。

2) A generational collector can offer more optimizations by not traversing every object in the managed heap. If a root or object refers to an object in an old generation, the garbage collector can ignore any of the older objects' inner references, decreasing the time required to build the graph of reachable objects. Of course, it is possible that an old object refers to a new object. So that these objects are examined, the collector can take advantage of the system's write-watch support (provided by the Win32 GetWriteWatch function in Kernel32.dll). This support lets the collector know which old objects (if any) have been written to since the last collection. These specific old objects can have their references checked to see if they refer to any new objects.

2)分代收集器可以通过不遍历托管堆中的每个对象来提供更多优化。 如果根或对象引用的是旧对象,则垃圾收集器可以忽略任何较旧对象的内部引用,从而减少了构建可访问对象图所需的时间。 当然,旧对象可能是指新对象。 为了检查这些对象,收集器可以利用系统的写监视支持(由Kernel32.dll中的Win32 GetWriteWatch函数提供)。 这种支持使收集器可以知道自上次收集以来已将哪些旧对象(如果有)写入其中。 可以检查这些特定的旧对象的引用,以查看它们是否引用了任何新对象。

Garbage Collection in Multithreaded Applications多线程应用程序中的垃圾回收

While understanding the mechanism of Garbage Collection, as above, we made a big assumption that, only one thread is running. Though in fact under normal circumstances, there would be multiple threads accessing the managed heap or manipulating objects allocated space on the managed heap.

如上所述,在了解垃圾回收机制的同时,我们做出了一个很大的假设,即只有一个线程在运行。 尽管实际上在正常情况下,还是会有多个线程访问托管堆或操纵对象在托管堆上分配的空间。

When the space is requested by a new object from one thread and the Collection is triggered, then it becomes important that other threads must not access any objects (including object references on its own stack) since the collector is likely to move these objects, changing their memory locations. Hence, when the garbage collector starts a collection, all threads executing managed code are suspended.

当一个新对象从一个线程请求空间并触发Collection时,其他线程不得访问任何对象(包括其自身堆栈上的对象引用)就变得很重要,因为收集器可能会移动这些对象,他们的记忆位置。 因此,当垃圾收集器开始收集时,所有执行托管代码的线程都将被挂起。

The runtime employs different mechanisms to safely suspend threads so as to keep the threads running as long as possible before the collection is performed. This is to reduce overhead as much as possible. The mechanisms that the garbage collector employs when applications have multiple threads are:

运行时使用不同的机制来安全地挂起线程,以便在执行收集之前使线程尽可能长时间地运行。 这是为了尽可能减少开销。 当应用程序具有多个线程时,垃圾收集器采用的机制是:

1.  Fully Interruptible Code

1.完全可中断的代码

When a collection starts, the collector suspends all application threads and determines –

收集开始时,收集器会挂起所有应用程序线程,并确定–

(i)   Where the thread was suspended, i.e. where in a method the thread stopped,

(i)线程被挂起的地方,即在某种方法中线程被停止的地方,

(ii)   What object references the code is currently accessing, and

(ii)哪个对象引用了当前正在访问的代码,并且

(iii)  Where those references are held in a variable or a CPU register, etc.

(iii)那些引用保存在变量或CPU寄存器等中的地方

2. Hijacking

2.劫持

When a collection occurs, the collector modifies the thread's stack, such that when a method returns, a special function is executed, to suspend the thread. When the collection is complete, the thread resumes and returns to the method that originally called it. This is called Thread Hijacking.

当发生收集时,收集器会修改线程的堆栈,以便当方法返回时,将执行特殊功能以挂起线程。 收集完成后,线程将恢复并返回到最初调用它的方法。 这称为线程劫持。

This thread hijacking allows threads that are executing unmanaged code to continue execution while a garbage collection is occurring. Please note, this does not cause any problem, since unmanaged code is not accessing objects on the managed heap unless the objects do not contain object references. If a thread that is currently executing unmanaged code returns to managed code, the thread is hijacked and is suspended until the Garbage Collection completes.

此线程劫持允许正在执行非托管代码的线程在发生垃圾回收时继续执行。 请注意,这不会造成任何问题,因为非托管代码不会访问托管堆上的对象,除非对象不包含对象引用。 如果当前正在执行非托管代码的线程返回到托管代码,则该线程将被劫持并挂起,直到垃圾回收完成。

3. Safe Points

3.安全点

While compiling a method, if the just-in-time compiler finds that a Garbage Collection is pending –

在编译方法时,如果即时编译器发现垃圾回收处于待处理状态,

(i)    It inserts a call to a function that suspends the thread,

(i)插入对暂停线程的函数的调用,

(ii)   First the Garbage Collection is completed, and

(ii)首先完成垃圾收集,然后

(iii)  Then the thread is resumed.

(iii)然后继续线程。

This position where the compiler inserts these method calls is called a Safe Point.

编译器在其中插入这些方法调用的位置称为安全点。

4. Synchronization-free Allocations

4.免同步分配

On a multiprocessor system, generation 0 of the managed heap is split into multiple memory areas using one area per thread. This allows multiple threads to make allocations simultaneously so that exclusive access to the heap is not required.

在多处理器系统上,每个线程使用一个区域将托管堆的第0代划分为多个内存区域。 这允许多个线程同时进行分配,因此不需要排他访问堆。

5. Scalable Collections

5.可扩展的集合

On a multiprocessor system running the server version of the execution engine (MSCorSvr.dll), the managed heap is split into several sections, one per CPU. When a collection is initiated, the collector has one thread per CPU; all threads collect their own sections simultaneously. The workstation version of the execution engine (MSCorWks.dll) doesn't support this feature.

在运行服务器版本的执行引擎(MSCorSvr.dll)的多处理器系统上,托管堆分为多个部分,每个CPU一个。 启动收集时,每个CPU收集器有一个线程;每个CPU都有一个线程。 所有线程同时收集自己的部分。 执行引擎的工作站版本(MSCorWks.dll)不支持此功能。

定案 (Finalization)

假设在对象由垃圾收集器进行收集之前,是否要为对象执行一段代码。 垃圾收集器的一项功能(称为“最终化”)使之成为可能。

Finalization allows a resource to be gracefully cleaned when it is being collected. By using finalization, a resource like a file or network connection, is able to clean itself up properly when the garbage collector decides to free the resource's memory. This is made possible by an override void Finalize () with clean up resource code in it. When the Garbage Collector recognizes that the class object is pretty much going to be a piece of garbage, it calls the Finalize() method.

最终确定允许在收集资源时优雅地对其进行清理。 通过使用终结处理,当垃圾收集器决定释放资源的内存时,诸如文件或网络连接之类的资源便能够正确清理自身。 这可以通过重写void Finalize()并在其中清理资源代码来实现。 当垃圾收集器识别出该类对象几乎是垃圾时,它将调用Finalize()方法。

public class objBase 
{
        public objBase() 
        {
        }

        protected override void Finalize() 
        {
                // Resource Cleanup Code 
        }
}

When we create and then instantiate an object using the 'new' operator. This ‘new’ operator first makes sure that the bytes required by the new object are available on the heap, i.e. the new object could fit on the heap. It then not only puts the object on the heap, but also checks if the object's type contains a Finalize method. If yes then a pointer to the object is placed on the Finalization Queue.

当我们使用“ new”运算符创建并实例化对象时。 该“新”运算符首先确保新对象所需的字节在堆上可用,即,新对象可以容纳在堆上。 然后,它不仅将对象放在堆上,还检查对象的类型是否包含Finalize方法。 如果是,则将指向该对象的指针放在终结队列上。

The Finalization Queue is an internal data structure (Queue) controlled by the garbage collector. Each entry in the queue points to an object that should have its Finalize method called before the object's memory can be reclaimed.

终结队列是由垃圾收集器控制的内部数据结构(队列)。 队列中的每个条目都指向一个对象,在可以回收该对象的内存之前,应调用该对象的Finalize方法。

E.g. when the objects K, O and P were created, the system detected that these objects had Finalize methods; hence after adding these objects to the heap, the pointers to these objects were added to the Finalization Queue.

例如,当创建对象K,O和P时,系统检测到这些对象具有Finalize方法。 因此,在将这些对象添加到堆之后,将指向这些对象的指针添加到完成队列。

Managed Heap with pointers in Finalizable Queue

When the garbage collector finds the objects B, K, O, Q and S to be garbage, it scans the Finalization Queue looking for pointers to these objects. If a pointer is found, it is removed from the Finalization Queue and added to the Freachable Queue.

当垃圾收集器发现对象B,K,O,Q和S为垃圾时,它将扫描终结队列以查找指向这些对象的指针。 如果找到了指针,则将其从完成队列中删除并添加到可到达队列中。

The Freachable Queue is another internal data structure (Queue) controlled by the garbage collector. Each pointer in the Freachable Queue identifies an object that is ready to have its Finalize method called.

Freachable Queue是由垃圾收集器控制的另一个内部数据结构(Queue)。 易碎队列中的每个指针都标识一个对象,该对象已准备好调用其Finalize方法。

Hence, we see that when the collection occurs, the memory occupied by objects B, O, and S is reclaimed because these objects did not have a Finalize method that needs to be called. However, the memory occupied by objects K and Q could not be reclaimed because their Finalize method has not been called yet and the pointers to both these objects are moved from the Finalization Queue to the Freachable Queue.

因此,我们看到当发生收集时,对象B,O和S占用的内存被回收,因为这些对象没有需要调用的Finalize方法。 但是,无法回收对象K和Q占用的内存,因为尚未调用它们的Finalize方法,并且指向这两个对象的指针已从Finalization Queue移到Freachable Queue。

Managed Heap with pointers in Freachable Queue

There is a special runtime thread dedicated to calling Finalize methods. Normally when the Freachable Queue is empty, this thread sleeps. But when the pointers are added to this queue, this thread awakes, removes each entry from the queue, and calls each object's Finalize method. Because of this, you should not execute any code in a Finalize method that makes any assumption about the thread that's executing the code. For example, avoid accessing thread local storage in the Finalize method.

有一个专用的运行时线程专用于调用Finalize方法。 通常,当“可到达队列”为空时,此线程进入睡眠状态。 但是,当将指针添加到此队列时,该线程将唤醒,从队列中删除每个条目,并调用每个对象的Finalize方法。 因此,您不应在Finalize方法中执行任何代码,该方法对执行代码的线程进行任何假设。 例如,避免在Finalize方法中访问线程本地存储。

In our example, we see that the pointers to objects K and Q have been moved to the Freachable Queue and their Finalize() method has been called by the thread.

在我们的示例中,我们看到指向对象K和Q的指针已移动到Freachable Queue,并且线程已调用了它们的Finalize()方法。

Now the next time the garbage collection occurs, the Garbage Collector finds the pointers to objects K and Q in the Freacheble Queue and realizes that these finalized objects are truly garbage, and hence it goes ahead and reclaims the memory held by the objects K and Q. Please note that two iterations of the Garbage Collection process are required to free the memory space occupied by the objects that require finalization. In reality, more than two collections may be necessary since the objects could get promoted to an older generation.

现在,下一次发生垃圾收集时,垃圾收集器在Freacheble队列中找到指向对象K和Q的指针,并意识到这些最终确定的对象是真正的垃圾,因此它继续进行并回收由对象K和Q保留的内存请注意,垃圾回收过程需要进行两次迭代才能释放需要终结的对象占用的内存空间。 实际上,可能需要两个以上的集合,因为这些对象可以提升为较老的一代。

Managed Heap after Finalization

So, we see that the Finalization process is quite fascinating, but I personally feel, it is not advisable to use this feature --

因此,我们看到“完成”过程非常有趣,但是我个人认为,不建议使用此功能-

When a garbage collection occurs, the Garbage collector looks for and identifies the objects that contain a Finalize method. These objects are promoted to older generations, which for the time being prevent the object's memory from being collected. Moreover, all the objects referred to directly or indirectly by these finalized objects, also get promoted to older generations.

发生垃圾收集时,垃圾收集器将查找并标识包含Finalize方法的对象。 这些对象被提升为较老的一代,这暂时阻止了对象的内存被收集。 而且,这些最终确定的对象直接或间接引用的所有对象也都晋升为上一代。



Finalizable objects take longer to allocate.

可终结对象需要更长的分配时间。



When a garbage collector is forced to execute the Finalize method of an object, it has a strong potential to significantly hurt the performance, as it would require each object to be finalized.

当垃圾回收器被迫执行对象的Finalize方法时,它有很大的潜力严重损害性能,因为这将需要对每个对象进行终结处理。



Finalizable objects may refer to other non-finalizable objects, prolonging their lifetime unnecessarily. In fact, you might want to consider breaking a type into two different types: a lightweight type with a Finalize method that doesn't refer to any other objects, and a separate type without a Finalize method that does refer to other objects.

终结对象可以引用其他不可终结的对象,从而不必要地延长了它们的寿命。 实际上,您可能需要考虑将一个类型分为两种类型:一种轻型类型,其具有不引用任何其他对象的Finalize方法,一个单独的类型,其类型不具有引用其他对象的Finalize方法。



We have no control over when a Finalize method would execute. The object may hold on to the resources until the next time the garbage collector runs.

我们无法控制何时执行Finalize方法。 该对象可能会保留资源,直到下一次垃圾收集器运行为止。



When an application terminates, some objects are still reachable and will not have their Finalize method called. This can happen if background threads are using the objects or if objects are created during application shutdown or AppDomain unloading. In addition, by default, Finalize methods are not called for unreachable objects when an application exits so that the application may terminate quickly. Of course, all operating system resources will be reclaimed, but any objects in the managed heap are not able to clean up gracefully. We can change this default behavior by calling the System.GC type's RequestFinalizeOnShutdown method. However, we should use this method with care since calling it means that our type is controlling a policy for the entire application.

当应用程序终止时,某些对象仍然可以访问,并且不会调用其Finalize方法。 如果后台线程正在使用对象,或者在应用程序关闭或AppDomain卸载期间创建了对象,则可能发生这种情况。 另外,默认情况下,在应用程序退出时,不可达对象不会调用Finalize方法,因此应用程序可能会Swift终止。 当然,将回收所有操作系统资源,但是托管堆中的任何对象都无法正常清理。 我们可以通过调用System.GC类型的RequestFinalizeOnShutdown方法来更改此默认行为。 但是,我们应该谨慎使用此方法,因为调用它意味着我们的类型正在控制整个应用程序的策略。



The runtime doesn't make any guarantees as to the order in which Finalize methods are called. For example, let's say there is an object that contains a pointer to an inner object. The garbage collector has detected that both objects are garbage. Furthermore, say that the inner object's Finalize method gets called first. Now, the outer object's Finalize method is allowed to access the inner object and call methods on it, but the inner object has been finalized and the results may be unpredictable. For this reason, it is strongly recommended that Finalize methods not access any inner, member objects.

运行时无法保证Finalize方法的调用顺序。 例如,假设有一个对象包含一个指向内部对象的指针。 垃圾收集器检测到两个对象都是垃圾。 此外,假设首先调用内部对象的Finalize方法。 现在,允许外部对象的Finalize方法访问内部对象并对其调用方法,但是内部对象已完成,并且结果可能无法预测。 因此,强烈建议Finalize方法不要访问任何内部成员对象。



外部资源呢? (What about External Resources?)

垃圾收集器有效地处理了从托管堆中释放资源的情况,但是只有在内存压力触发收集时才启动收集。 管理有限的外部资源(例如数据库连接,Windows句柄或外部文件等)的类又如何呢? 等待触发垃圾回收来清理数据库连接或文件句柄会严重降低系统性能。

Dispose Method

处置方法

Microsoft has provided another alternative whereby we could use the IDisposable interface. When we implement this interface, we get a Dispose() method which can be used to convey a message to the CLR to take care of objects that are no longer required by the application.

Microsoft提供了另一种替代方法,使我们可以使用IDisposable接口。 当实现此接口时,我们将获得一个Dispose()方法,该方法可用于将消息传达给CLR,以处理应用程序不再需要的对象。

This method can be used to close or release unmanaged resources such as files, streams, and handles held by an instance of the class that implements this interface. This method is, by convention, used for all tasks associated with freeing resources held by an object, or preparing an object for reuse.

此方法可用于关闭或释放非托管资源,例如由实现此接口的类的实例持有的文件,流和句柄。 按照惯例,此方法用于与释放对象所拥有的资源或准备对象以供重用相关的所有任务。

When implementing this method, objects must seek to ensure that all held resources are freed by propagating the call through the containment hierarchy. For example, if an object A allocates an object B, and object B allocates an object C, then A's Dispose implementation must call Dispose on B, which must in turn call Dispose on C. Objects must also call the Dispose method of their base class if the base class implements IDisposable.

在实现此方法时,对象必须设法通过在包含层次结构中传播调用来确保释放所有保留的资源。 例如,如果对象A分配了对象B,而对象B分配了对象C,则A的Dispose实现必须调用B上的Dispose,后者又必须调用C上的Dispose。对象还必须调用其基类的Dispose方法。如果基类实现IDisposable。

If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times. Dispose can throw an exception if an error occurs because a resource has already been freed and Dispose had not been called previously.

如果对象的Dispose方法被多次调用,则该对象必须忽略第一个之后的所有调用。 如果多次调用其Dispose方法,则该对象不得引发异常。 如果由于已释放资源并且以前未调用过Dispose而发生错误,则Dispose可能引发异常。

Because the Dispose method must be called explicitly, objects that implement IDisposable must also implement a finalizer to handle freeing resources when Dispose is not called. By default, the garbage collector will automatically call an object's finalizer prior to reclaiming its memory. However, once the Dispose method has been called, it is typically unnecessary for the garbage collector to call the disposed object's finalizer. To prevent automatic finalization, Dispose implementations can call the GC.SuppressFinalize method.

因为必须显式调用Dispose方法,所以实现IDisposable的对象还必须实现终结器,以在不调用Dispose时处理释放资源。 默认情况下,垃圾回收器将在回收对象的内存之前自动调用其终结器。 但是,一旦调用了Dispose方法,垃圾收集器通常就不需要调用已处理对象的终结器。 为了防止自动完成,Dispose实现可以调用GC.SuppressFinalize方法。

使用System.GC类 (Using the System.GC Class)

System.GC类型使您的应用程序可以直接控制垃圾收集器。 它用于访问.NET框架公开的垃圾收集机制。 此类包含以下有用的方法:

GC.MaxGeneration, is used to query the maximum generation supported by the managed heap. By default, the GC.MaxGeneration property always returns 2. GC.MaxGeneration ,用于查询托管堆支持的最大生成量。 默认情况下,GC.MaxGeneration属性始终返回2。

GC.SuppressFinalize was described earlier in the column; this method inhibits finalization for an object. Call this method if you have already released external resources owned by an object. GC.SuppressFinalize在本专栏的前面已经介绍过; 此方法禁止最终确定对象。 如果您已经释放了对象拥有的外部资源,请调用此方法。

GC.Collect comes in two versions. The version that has no parameter performs a full collection on all generations in the managed heap. Another version accepts an integer value representing the generation to be collected. You'll rarely need to call this method, as the garbage collector automatically runs when needed.

          void GC.Collect(Int32 Generation)
          void GC.Collect()

The first method allows you to specify which generation to collect. You may pass any integer from 0 to GC.MaxGeneration, inclusive. Passing 0 causes generation 0 to be collected; passing 1 cause generation 1 and 0 to be collected; and passing 2 causes generation 2, 1, and 0 to be collected. The version of the Collect method that takes no parameters forces a full collection of all generations and is equivalent to calling:



          GC.Collect(GC.MaxGeneration);

GC.Collect有两个版本。 没有参数的版本将对托管堆中的所有世代执行完整收集。 另一个版本接受表示要收集的世代的整数值。 您几乎不需要调用此方法,因为垃圾收集器会在需要时自动运行。



第一种方法允许您指定要收集的世代。 您可以将0范围内的任何整数传递给GC.MaxGeneration(含)。 传递0将导致生成0被收集; 传递1导致收集第1代和第0代; 传递2会导致生成2、1、0。 不带参数的Collect方法的版本强制所有世代的完整集合,等效于调用:




GC.GetGeneration returns the generation number for an object passed as a parameter. This method is useful when debugging or tracing for performance reasons, but has limited value in most applications.

          Int32 GetGeneration(Object obj)
          Int32 GetGeneration(WeakReference wr)

The first version of GetGeneration takes an object reference as a parameter, and the second version takes a WeakReference reference as a parameter. Of course, the value returned will be somewhere between 0 and GC.MaxGeneration, inclusive.


GC.GetGeneration返回作为参数传递的对象的世代号。 由于性能原因,此方法在调试或跟踪时很有用,但在大多数应用程序中价值有限。



GetGeneration的第一个版本将对象引用作为参数,而第二个版本将WeakReference引用作为参数。 当然,返回的值将介于0到GC.MaxGeneration之间(含)。


GC.GetTotalMemory returns the amount of memory allocated in the heap. This number is not exact due to the way the managed heap works, but a close approximation can be obtained if true is passed as a parameter. This causes a collection to be performed before the memory usage is calculated. GC.GetTotalMemory返回在堆中分配的内存量。 由于托管堆的工作方式,此数字不准确,但是如果将true作为参数传递,则可以获得近似值。 这将导致在计算内存使用量之前执行收集。

WaitForPendingFinalizers, this method simply suspends the calling thread until the thread processing the Freachable Queue has emptied the queue, calling each object's Finalize method. In most applications, it is unlikely that you will ever have to call this method. WaitForPendingFinalizers ,此方法只是挂起调用线程,直到处理Freachable Queue的线程清空了队列,然后调用每个对象的Finalize方法。 在大多数应用程序中,您几乎不必调用此方法。

参考 (Reference)

我从MSDN文章中获得了一些参考; Jeffrey Richter的“垃圾收集:Microsoft .NET Framework中的自动内存管理” http://msdn.microsoft.com/en-us/library/bb985010.aspx http://msdn.microsoft.com/zh-CN/library/bb985010.aspx

I found this article quite informative and hence tried to build upon it.

我发现这篇文章内容丰富,因此尝试以此为基础。

Garbage-Collection-Memory-Manage.docx 垃圾收集内存管理.docx

翻译自: https://www.experts-exchange.com/articles/3251/Garbage-Collection-Memory-Management-in-Net.html

内存管理与垃圾回收机制

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值