自动内存管理Automatic Memory Management

自动内存管理是公共语言运行时在托管执行过程中提供的服务之一。Automatic memory management is one of the services that the Common Language Runtime provides during Managed Execution. 公共语言运行时的垃圾回收器为应用程序管理内存的分配和释放。The Common Language Runtime's garbage collector manages the allocation and release of memory for an application. 对开发人员而言,这就意味着在开发托管应用程序时不必编写执行内存管理任务的代码。For developers, this means that you do not have to write code to perform memory management tasks when you develop managed applications. 自动内存管理可解决常见问题,例如,忘记释放对象并导致内存泄漏,或尝试访问已释放对象的内存。Automatic memory management can eliminate common problems, such as forgetting to free an object and causing a memory leak, or attempting to access memory for an object that has already been freed. 本节描述垃圾回收器如何分配和释放内存。This section describes how the garbage collector allocates and releases memory.

分配内存Allocating Memory

初始化新进程时,运行时会为进程保留一个连续的地址空间区域。When you initialize a new process, the runtime reserves a contiguous region of address space for the process. 这个保留的地址空间被称为托管堆。This reserved address space is called the managed heap. 托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。The managed heap maintains a pointer to the address where the next object in the heap will be allocated. 最初,该指针设置为指向托管堆的基址。Initially, this pointer is set to the managed heap's base address. 托管堆上包含了所有引用类型All reference types are allocated on the managed heap. 应用程序创建第一个引用类型时,将为托管堆的基址中的类型分配内存。When an application creates the first reference type, memory is allocated for the type at the base address of the managed heap. 应用程序创建下一个对象时,垃圾回收器在紧接第一个对象后面的地址空间内为它分配内存。When the application creates the next object, the garbage collector allocates memory for it in the address space immediately following the first object. 只要地址空间可用,垃圾回收器就会继续以这种方式为新对象分配空间。As long as address space is available, the garbage collector continues to allocate space for new objects in this manner.

从托管堆中分配内存要比非托管内存分配速度快。Allocating memory from the managed heap is faster than unmanaged memory allocation. 由于运行时通过为指针添加值来为对象分配内存,所以这几乎和从堆栈中分配内存一样快。Because the runtime allocates memory for an object by adding a value to a pointer, it is almost as fast as allocating memory from the stack. 另外,由于连续分配的新对象在托管堆中是连续存储,所以应用程序可以快速访问这些对象。In addition, because new objects that are allocated consecutively are stored contiguously in the managed heap, an application can access the objects very quickly.

释放内存Releasing Memory

垃圾回收器的优化引擎根据所执行的分配决定执行回收的最佳时间。The garbage collector's optimizing engine determines the best time to perform a collection based on the allocations being made. 垃圾回收器在执行回收时,会释放应用程序不再使用的对象的内存。When the garbage collector performs a collection, it releases the memory for objects that are no longer being used by the application. 它通过检查应用程序的根来确定不再使用的对象。It determines which objects are no longer being used by examining the application's roots. 每个应用程序都有一组根。Every application has a set of roots. 每个根或者引用托管堆中的对象,或者设置为空。Each root either refers to an object on the managed heap or is set to null. 应用程序的根包含线程堆栈上的静态字段、局部变量和参数以及 CPU 寄存器。An application's roots include static fields, local variables and parameters on a thread's stack, and CPU registers. 垃圾回收器可以访问由实时 (JIT) 编译器和运行时维护的活动根的列表。The garbage collector has access to the list of active roots that the just-in-time (JIT) compiler and the runtime maintain. 垃圾回收器对照此列表检查应用程序的根,并在此过程中创建一个图表,在其中包含所有可从这些根中访问的对象。Using this list, it examines an application's roots, and in the process creates a graph that contains all the objects that are reachable from the roots.

不在该图表中的对象将无法从应用程序的根中访问。Objects that are not in the graph are unreachable from the application's roots. 垃圾回收器会考虑无法访问的对象垃圾,并释放为它们分配的内存。The garbage collector considers unreachable objects garbage and will release the memory allocated for them. 在回收中,垃圾回收器检查托管堆,查找无法访问对象所占据的地址空间块。During a collection, the garbage collector examines the managed heap, looking for the blocks of address space occupied by unreachable objects. 发现无法访问的对象时,它就使用内存复制功能来压缩内存中可以访问的对象,释放分配给不可访问对象的地址空间块。As it discovers each unreachable object, it uses a memory-copying function to compact the reachable objects in memory, freeing up the blocks of address spaces allocated to unreachable objects. 在压缩了可访问对象的内存后,垃圾回收器就会做出必要的指针更正,以便应用程序的根指向新地址中的对象。Once the memory for the reachable objects has been compacted, the garbage collector makes the necessary pointer corrections so that the application's roots point to the objects in their new locations. 它还将托管堆指针定位至最后一个可访问对象之后。It also positions the managed heap's pointer after the last reachable object. 请注意,只有在回收发现大量的无法访问的对象时,才会压缩内存。Note that memory is compacted only if a collection discovers a significant number of unreachable objects. 如果托管堆中的所有对象均未被回收,则不需要压缩内存。If all the objects in the managed heap survive a collection, then there is no need for memory compaction.

为了改进性能,运行时为单独堆中的大型对象分配内存。To improve performance, the runtime allocates memory for large objects in a separate heap. 垃圾回收器会自动释放大型对象的内存。The garbage collector automatically releases the memory for large objects. 但是,为了避免移动内存中的大型对象,不会压缩此内存。However, to avoid moving large objects in memory, this memory is not compacted.

级别和性能Generations and Performance

为优化垃圾回收器的性能,将托管堆分为三代:第 0 代、第 1 代和第 2 代。To optimize the performance of the garbage collector, the managed heap is divided into three generations: 0, 1, and 2. 运行时的垃圾回收算法基于以下几个普遍原理,这些垃圾回收方案的原理已在计算机软件业通过实验得到了证实。The runtime's garbage collection algorithm is based on several generalizations that the computer software industry has discovered to be true by experimenting with garbage collection schemes. 首先,压缩托管堆的一部分内存要比压缩整个托管堆速度快。First, it is faster to compact the memory for a portion of the managed heap than for the entire managed heap. 其次,较新的对象生存期较短,而较旧的对象生存期则较长。Secondly, newer objects will have shorter lifetimes and older objects will have longer lifetimes. 最后,较新的对象趋向于相互关联,并且大致同时由应用程序访问。Lastly, newer objects tend to be related to each other and accessed by the application around the same time.

运行时的垃圾回收器将新对象存储在第 0 级中。The runtime's garbage collector stores new objects in generation 0. 在应用程序生存期的早期创建的对象如果未被回收,则被升级并存储在第 1 级和第 2 级中。Objects created early in the application's lifetime that survive collections are promoted and stored in generations 1 and 2. 本主题中稍后介绍了对象升级过程。The process of object promotion is described later in this topic. 因为压缩托管堆的一部分要比压缩整个托管堆速度快,所以此方案允许垃圾回收器在每次执行回收时释放特定级别的内存,而不是整个托管堆的内存。Because it is faster to compact a portion of the managed heap than the entire heap, this scheme allows the garbage collector to release the memory in a specific generation rather than release the memory for the entire managed heap each time it performs a collection.

实际上,垃圾回收器在第 0 级托管堆已满时执行回收。In reality, the garbage collector performs a collection when generation 0 is full. 如果应用程序在第 0 级托管堆已满时尝试新建对象,垃圾回收器将会发现第 0 级托管堆中没有可分配给该对象的剩余地址空间。If an application attempts to create a new object when generation 0 is full, the garbage collector discovers that there is no address space remaining in generation 0 to allocate for the object. 垃圾回收器执行回收,尝试为对象释放第 0 级托管堆中的地址空间。The garbage collector performs a collection in an attempt to free address space in generation 0 for the object. 垃圾回收器从检查第 0 级托管堆中的对象(而不是托管堆中的所有对象)开始执行回收。The garbage collector starts by examining the objects in generation 0 rather than all objects in the managed heap. 这是最有效的途径,因为新对象的生存期往往较短,并且期望在执行回收时,应用程序不再使用第 0 级托管堆中的许多对象。This is the most efficient approach, because new objects tend to have short lifetimes, and it is expected that many of the objects in generation 0 will no longer be in use by the application when a collection is performed. 另外,单独回收第 0 级托管堆通常可以回收足够的内存,这样,应用程序便可以继续创建新对象。In addition, a collection of generation 0 alone often reclaims enough memory to allow the application to continue creating new objects.

垃圾回收器执行第 0 级托管堆的回收后,会压缩可访问对象的内存,如本主题前面的释放内存中所述。After the garbage collector performs a collection of generation 0, it compacts the memory for the reachable objects as explained in Releasing Memory earlier in this topic. 然后,垃圾回收器升级这些对象,并考虑第 1 级托管堆的这一部分。The garbage collector then promotes these objects and considers this portion of the managed heap generation 1. 因为未被回收的对象往往具有较长的生存期,所以将它们升级至更高的级别很有意义。Because objects that survive collections tend to have longer lifetimes, it makes sense to promote them to a higher generation. 因此,垃圾回收器在每次执行第 0 级托管堆的回收时,不必重新检查第 1 级和第 2 级托管堆中的对象。As a result, the garbage collector does not have to reexamine the objects in generations 1 and 2 each time it performs a collection of generation 0.

在执行第 0 级托管堆的首次回收并把可访问的对象升级至第 1 级托管堆后,垃圾回收器将考虑第 0 级托管堆的其余部分。After the garbage collector performs its first collection of generation 0 and promotes the reachable objects to generation 1, it considers the remainder of the managed heap generation 0. 它将继续为第 0 级托管堆中的新对象分配内存,直至第 0 级托管堆已满并需执行另一回收为止。It continues to allocate memory for new objects in generation 0 until generation 0 is full and it is necessary to perform another collection. 这时,垃圾回收器的优化引擎会决定是否需要检查较旧的级别中的对象。At this point, the garbage collector's optimizing engine determines whether it is necessary to examine the objects in older generations. 例如,如果第 0 级托管堆的回收没有回收足够的内存,不能使应用程序成功完成创建新对象的尝试,垃圾回收器就会先执行第 1 级托管堆的回收,然后再执行第 2 级托管堆的回收。For example, if a collection of generation 0 does not reclaim enough memory for the application to successfully complete its attempt to create a new object, the garbage collector can perform a collection of generation 1, then generation 2. 如果这样仍不能回收足够的内存,垃圾回收器将执行第 2、1 和 0 级托管堆的回收。If this does not reclaim enough memory, the garbage collector can perform a collection of generations 2, 1, and 0. 每次回收后,垃圾回收器都会压缩第 0 级托管堆中的可访问对象并将它们升级至第 1 级托管堆。After each collection, the garbage collector compacts the reachable objects in generation 0 and promotes them to generation 1. 第 1 级托管堆中未被回收的对象将会升级至第 2 级托管堆。Objects in generation 1 that survive collections are promoted to generation 2. 由于垃圾回收器只支持三个级别,因此第 2 级托管堆中未被回收的对象会继续保留在第 2 级托管堆中,直到在将来的回收中确定它们为无法访问为止。Because the garbage collector supports only three generations, objects in generation 2 that survive a collection remain in generation 2 until they are determined to be unreachable in a future collection.

为非托管资源释放内存Releasing Memory for Unmanaged Resources

对于应用程序创建的大多数对象,可以依赖垃圾回收器自动执行必要的内存管理任务。For the majority of the objects that your application creates, you can rely on the garbage collector to automatically perform the necessary memory management tasks. 但是,非托管资源需要显式清除。However, unmanaged resources require explicit cleanup. 最常用的非托管资源类型是包装操作系统资源的对象,例如,文件句柄、窗口句柄或网络连接。The most common type of unmanaged resource is an object that wraps an operating system resource, such as a file handle, window handle, or network connection. 虽然垃圾回收器可以跟踪封装非托管资源的托管对象的生存期,但却无法具体了解如何清理资源。Although the garbage collector is able to track the lifetime of a managed object that encapsulates an unmanaged resource, it does not have specific knowledge about how to clean up the resource. 创建封装非托管资源的对象时,建议在公共 Dispose 方法中提供必要的代码以清理非托管资源。When you create an object that encapsulates an unmanaged resource, it is recommended that you provide the necessary code to clean up the unmanaged resource in a public Dispose method. 通过提供 Dispose 方法,对象的用户可以在使用完对象后显式释放其内存。By providing a Dispose method, you enable users of your object to explicitly free its memory when they are finished with the object. 使用封装非托管资源的对象时,应该了解 Dispose 并在必要时调用它。When you use an object that encapsulates an unmanaged resource, you should be aware of Dispose and call it as necessary. 有关清理非托管资源的详细信息和实现 Dispose 的设计模式示例,请参见垃圾回收For more information about cleaning up unmanaged resources and an example of a design pattern for implementing Dispose, see Garbage Collection.

请参阅See also