Guru of the Week 條款09:記憶體管理(上篇)
GotW #09 Memory Management - Part I
著者:Herb Sutter
翻譯:kingofark
[宣告]:本文內容取自www.gotw.ca網站上的Guru of the Week欄目,其著作權歸原著者本人所有。譯者kingofark在未經原著者本人同意的情況下翻譯本文。本翻譯內容僅供自學和參考用,請所有閱讀過本文的人不要擅自轉載、傳播本翻譯內容;下載本翻譯內容的人請在閱讀瀏覽後,立即刪除其備份。譯者kingofark對違反上述兩條原則的人不負任何責任。特此宣告。
Revision 1.0
Guru of the Week 條款09:記憶體管理(上篇)<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
難度:3 / 10
(本條款介紹C++中幾個主要記憶體區域的基本知識。條款10將會繼續本條款的話題,深入討論一些記憶體管理的問題。)
[問題]
C++擁有幾個不同的記憶體區域,用來儲存物件或其它型別的值。每一個區域都有其各自的特點。
請叫出儘可能多的記憶體區域的名稱,並分析每一個區域的效能特徵,描述儲存在其中的物件的生存週期。
舉例:堆疊區(stack)儲存自動變數(automatic variables),包括內建型別和類物件等。
[解答]
下面總結出了C++程式主要的記憶體區域。注意,有些名稱(比如heap)可能與C++標準中的叫法不一樣。
[常量資料(const data
常量資料區儲存字串等在編譯期間就能確定的值。類物件不能存在於這個區域中。在程式的整個生存週期內,區域中的資料都是可用的。
區域內所有的資料都是隻讀的,任何企圖修改本區域資料的行為都會造成無法預料的後果。之所以會如此,是因為在實際的實現當中,即使是最底層的內部儲存格式也受制於所實現的特定的優化方案。例如,一種編譯器完全可以把字串存放在幾個重疊的物件裡面——只要實現者願意的話。
[棧(stack)區:]
棧區儲存自動變數(automatic variables)。一般來說,棧區的分配操作要比動態儲存區(比如堆(heap)或者自由儲存區(free store))快得多,這是因為棧區的分配只涉及到一個指標的遞增,而動態儲存區的分配涉及到較為複雜的管理機制。棧區中,記憶體一旦被分配,物件就立即被構造好了;物件一旦被銷燬,分配的記憶體也立即被收回(譯註:這裡作者用了“去配(
[自由儲存區(free store):]
自由儲存區(free store)是C++兩個動態記憶體區域之一,使用new和delete來予以分配和釋放(freed)。在自由儲存區(free store)中,物件的生存週期可以比存放它的記憶體區的生存週期短;這也就是說,我們可以獲得一片記憶體區而不用馬上對其進行初始化;同時,在物件被銷燬之後,也不用馬上收回其佔用的記憶體區。在物件被銷燬而其佔用的記憶體區還未被收回的這段時間內,我們可以通過void*型的指標訪問這片區域,但是其原始物件的非靜態成員以及成員函式(即使我們知道了它們的地址)都不能被訪問或者操縱。
[堆(heap)區:]
堆(heap)區是另一個動態儲存區域,使用malloc、free以及一些相關變數來進行分配和回收。要注意,雖然在特定的編譯器裡預設的全域性運算子new和delete也許會按照malloc和free的方式來被實現,但是堆(heap)與自由儲存區(free store)是不同的——在某一個區域內被分配的記憶體不可能在另一個區域內被安全的回收。堆(heap)中被分配的記憶體一般用於存放在使用new的構造過程中和顯式(explicit)的析構過程中涉及到的類物件。堆中物件的生存週期與自由儲存區(free store)中的類似。
[全域性/靜態區(Global/Static):]
全域性的或靜態的變數和物件所佔用的記憶體區域在程式啟動(startup)的時候才被分配,而且可能直到程式開始執行的時候才被初始化。比如,函式中的靜態變數就是在程式第一次執行到定義該變數的程式碼時才被初始化的。對那些跨越了翻譯單元(translation unit)的全域性變數進行初始化操作的順序是沒有被明確定義的,因而需要特別注意管理全域性物件(包括靜態類物件)之間的依賴關係。最後,和前面講的一樣,全域性/靜態區(Global/Static)中沒有被初始化的物件儲存區域可以通過void*來被訪問和操縱,但是隻要是在物件真正的生存週期之外,非靜態成員和成員函式是無法被使用或者引用的。
[關於“堆(heap)vs.自由儲存區(free store)”]:本條款中我們將堆(heap)和自由儲存區(free store)區分開來,是因為在C++標準草案中,關於這兩種區域是否有聯絡的問題一直很謹慎的沒有予以詳細說明。比如當記憶體在通過delete運算符進行回收時,18.4.1.1中說道:
It is unspecified under what conditions part or all of such reclaimed storage is allocated by a subsequent call to operator new or any of calloc, malloc, or realloc, declared in <cstdlib>.
[關於在何種情況下,這種記憶體區域的部分或全部才會通過後續的對new(或者是在<cstdlib>裡宣告的calloc,malloc,realloc中的任何一個)的呼叫來被分配的問題,在此不作詳細規定,不予詳述。]
同樣,在一個特定的實現中,到底new和delete是按照malloc和free來實現的,或者反過來malloc和free是按照new和delete來實現的,這也沒有定論。簡單地說吧,這兩種記憶體區域運作方式不一樣,訪問方式也不一樣——所以嘛,當然應該被當成不一樣的兩個東西來使用了!