Automatic Memory ManagementAutomatic Memory Management

自動記憶體管理是 Common Language Runtime 在 Managed 執行期間所提供的一項服務。Automatic memory management is one of the services that the Common Language Runtime provides during Managed Execution. Common Language Runtime 的記憶體回收行程會管理應用程式記憶體的配置和釋放。The Common Language Runtime's garbage collector manages the allocation and release of memory for an application. 這表示開發人員在開發 Managed 應用程式時,不需要撰寫程式碼來執行記憶體管理工作。For developers, this means that you do not have to write code to perform memory management tasks when you develop managed applications. 自動記憶體管理可排除一些常見的問題,例如,忘記釋放物件而造成記憶體流失 (Memory Leak),或嘗試存取已經釋放物件的記憶體。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

當您初始化新的處理序 (Process) 時,Runtime 會保留一塊連續的位址空間區域,供處理序使用。When you initialize a new process, the runtime reserves a contiguous region of address space for the process. 這塊保留的位址空間稱為 Managed 堆積 (Heap)。This reserved address space is called the managed heap. Managed 堆積會保留即將配置給堆積中下一個物件的位址指標。The managed heap maintains a pointer to the address where the next object in the heap will be allocated. 剛開始會將這個指標設定為 Managed 堆積的基底位址 (Base Address)。Initially, this pointer is set to the managed heap's base address. 所有參考類型都是在 Managed 堆積上進行配置。All reference types are allocated on the managed heap. 當應用程式建立第一個參考型別時,會為該型別配置 Managed 堆積基底位址的記憶體。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.

從 Managed 堆積中配置記憶體要比 Unmanaged 記憶體配置快。Allocating memory from the managed heap is faster than unmanaged memory allocation. 由於 Runtime 是用增加指標值的方式為物件配置記憶體,因此速度幾乎和從堆疊中配置記憶體一樣快。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. 此外,由於連續配置的新物件是連續儲存在 Managed 堆積中,因此應用程式可以非常快速地存取這些物件。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. 它會檢查應用程式的根目錄 (Root),判斷哪些物件已不再使用。It determines which objects are no longer being used by examining the application's roots. 每一個應用程式都有一組根目錄。Every application has a set of roots. 每一個根目錄都會參考 Managed 堆積上的物件,要不然就是設定為 Null。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. 記憶體回收行程可以存取 Just-in-Time (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. 在回收期間,記憶體回收行程會檢查 Managed 堆積,尋找無法取得的物件所佔用的位址空間區塊。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. 它也會將 Managed 堆積的指標放在最後取得物件的後面。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. 如果 Managed 堆積中的所有物件在回收之後都存留下來,就不需要壓縮記憶體。If all the objects in the managed heap survive a collection, then there is no need for memory compaction.

為了改善效能,Runtime 會為大型物件配置不同堆積中的記憶體。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. 首先,壓縮部分 Managed 堆積的記憶體比壓縮整個 Managed 堆積要快。First, it is faster to compact the memory for a portion of the managed heap than for the entire managed heap. 其次,較新物件的存留期 (Lifetime) 較短,較舊物件的存留期較長。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. 由於壓縮部分 Managed 堆積比壓縮整個堆積要快,因此這種配置允許記憶體回收行程釋放指定層代的記憶體,而不是在每次執行回收時釋放整個 Managed 堆積的記憶體。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 個層代中的物件開始,而不是檢查 Managed 堆積中的所有物件。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. 然後,記憶體回收行程會提升這些物件,並考慮 Managed 堆積第 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 個層代之後,它會考慮 Managed 堆積第 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.

釋放 Unmanaged 資源的記憶體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. 但是,Unmanaged 資源需要明確清除。However, unmanaged resources require explicit cleanup. 最常見的 Unmanaged 資源類型就是包裝作業系統資源 (例如檔案控制代碼、視窗控制代碼或網路連接) 的物件。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. 雖然記憶體回收行程能夠追蹤封裝 Unmanaged 資源的 Managed 物件存留期,但是它並沒有關於如何清除資源的相關資訊。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. 當建立封裝 Unmanaged 資源的物件時,建議您提供必要的程式碼,在公用 Dispose 方法中清除 Unmanaged 資源。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. 當您使用封裝 Unmanaged 資源的物件時,應留意 Dispose 的用法,並在必要時加以呼叫。When you use an object that encapsulates an unmanaged resource, you should be aware of Dispose and call it as necessary. 如需清除 Unmanaged 資源的詳細資訊,以及實作 Dispose 的設計模式範例,請參閱記憶體回收For more information about cleaning up unmanaged resources and an example of a design pattern for implementing Dispose, see Garbage Collection.

另請參閱See also