2016-01-26 27 views
0

我想弄清楚什麼是組件或模塊(可能屬於OS?),它們實際上是在應用程序或進程運行時執行這些操作並專門運行命令delete[] X真正發生的事情和誰負責刪除[] X命令的調用?

我讀到delete[] X後,發現我的問題,我知道編譯器負責(根據它的實現)知道要刪除多少個對象X。但是,編譯器在運行時並不「活躍」!我的意思是,在編譯時,編譯器不知道用戶在新命令中需要多少內存,所以在刪除時也不會這樣做,所以程序實際運行時在運行時發生了什麼?

我讀到的答案之一是所謂的運行時系統,它是什麼?是否連接到CPU - 因爲CPU最終執行命令...或者操作系統?

我看到的另一個答案是「由系統的分配器完成」(How does delete[] know how much memory to delete?) - 這個組件(OS,CPU)又在哪裏?

回答

-1

當您使用new關鍵字時,程序會從堆中的操作系統請求一塊內存以保存該對象。指向該內存空間的指針返回。如果不使用new,編譯器會將對象放在堆棧上,並且在編譯期間,這些對象的內存將在堆棧上對齊。使用new創建的任何對象都需要在不再需要時刪除,因此重要的是不要丟失指向堆塊的原始指針,因此您可以對其調用delete。當你使用delete[]時,它將釋放數組中的所有塊。如果您創建char* anarray = new char[128],則使用delete []的示例,如果您的確使用了string *str = new string(),則會使用delete,因爲字符串被稱爲對象,並且char *是指向數組的指針。

編輯:某些對象重載刪除操作員,以便你的對象可以支持動態存儲器的適當的釋放,所以對象可以負責確定

2

C++運行時(間接地)使用Operating System原語到它的行爲更改運行程序的processvirtual address space

瞭解更多關於computer architectureCPU modesoperating systemsOS kernelssystem callsinstruction setsmachine codeobject codelinkersrelocationname manglingcompilersvirtual memory

在我的Linux系統,new(由C++標準庫提供)以上malloc(3)(由C標準庫提供),其可以調用mmap(2)系統調用(在內核中實現),其改變所述虛擬地址空間一般建(通過處理MMU)。並且delete(來自C++標準庫)一般建在free(3)以上,其中可能是,調用munmap(2)系統調用會改變虛擬地址空間。

事情更加複雜的細節:

  • new是有分配的內存與malloc

  • delete釋放內存之前被調用析構函數與free

  • 後調用構造函數

    free通常將釋放的內存區域標記爲將來可重用的malloc(所以通常munmap釋放內存

  • 所以前malloc通常重新使用以前釋放存儲器區域請求更多的地址空間(使用mmap)從內核

  • 數組new[]delete[],存儲器區域包含所述陣列的尺寸,並且構造(new[])或析構函數(delete[])被稱爲在一個循環

  • 技術上,當代碼SomeClass*p = new SomeClass(12);的內存使用::operator new(它調用malloc)第一分配,再SomeClass調用構造函數用12作爲參數

  • 當你的代碼delete p;SomeClass調用析構函數,然後將內存使用::operator delete發佈(這就要求free

BTW,Linux系統是由free software,所以我強烈建議你安裝你的機器上的一些Linux發行版,並使用它。因此,您可以研究內核的libc(標準C庫)的源代碼libstdc++(標準C++庫,它是GCC編譯器源代碼的一部分,但是與您的程序鏈接)。你也可以通過strace(1)你的C++程序和流程來了解它在做什麼system calls

如果使用GCC,您可以通過g++ -Wall -O -fverbose-asm -S foo.cc產生的foo.s彙編文件編譯你foo.cc C++源文件獲取生成的彙編代碼。您還可以在編譯器中使用g++ -Wall -O -fdump-tree-gimple -c foo.cc(您將獲得一些foo.cc.*.gimple以及許多其他GCC轉儲文件)獲得編譯器中的中間Gimple internal representation的一些文本視圖。你甚至可以使用GCC MELT工具(我設計並實現了它的大部分; useg++ -fplugin=melt -fplugin-arg-melt-mode=findgimple)在Gimple表示中搜索一些東西。

標準C++庫具有內部不變式和約定,並且C++編譯器在發佈彙編代碼時負責跟隨它們。所以編譯器和它的標準C++庫是共同設計和編寫的密切合作(並且你的C++庫實現中的一些骯髒的技巧需要編譯器支持,也許通過編譯器內置等等)。這不是特定於C++的:Ocaml人員還共同設計和共同實施Ocaml語言及其標準庫。C++標準庫libstdc++,C標準庫libc,操作系統(以及最底層的硬件,包括MMU)。所有這些都是實現細節,C++11語言標準沒有真正提到它們。

+0

我認爲你的回答引導我想要了解的內容。如果我做得對,有幾層代碼,包括使用mmap func的內核代碼(Linux,Windows等),所有這些「代碼」實際上編譯成彙編「代碼」,因此CPU可以運行它。至於刪除 - 編譯代碼已經在彙編之後。現在,當進程(代碼)被加載到內存時,CPU實際運行它。 **現在,在已經加載並運行的進程的這個階段中 - 誰負責處理內存? – StackUser

+0

你看起來很困惑,你應該花幾天的時間閱讀幾本有關計算機體系結構,指令集,操作系統設計,編譯器的書籍。我們無法在幾段中解釋所有這些。 –

+0

好的,你能給我一些出發點,好書名稱,鏈接或特定詞的種類來尋找一個開始? – StackUser

2

編譯器負責在需要時生成代碼delete。發生時無需運行。生成的代碼可能會是一個函數調用,做的東西沿着這些路線的例行:

void delete_arr(object *ptr) 
{ 
    size_t *actual_start = ((size_t *)ptr) - 1; 
    int count = *actual_start; 

    for (int i = count-1; i >= 0; i--) 
     destruct(ptr[i]); 

    free(actual_start); 
} 

new[]被調用,它實際保存的元素個數已分配內存的未來。當您撥打delete[]時,它會查找數量,然後刪除該數量的元素。

提供這些功能的庫稱爲C++標準庫或C++運行時環境。標準並沒有說什麼構成運行時,所以定義可能會有所不同,但要點是它需要支持運行C++代碼。

1

這可能是helpfull

  • 每次調用全球::運算符new(),它會採取傳遞的對象大小,並添加額外的數據的大小
  • 它會分配一個內存塊在前面的步驟
  • 推導出大小會抵消指針不會有額外的數據並返回所佔據塊的一部分偏移值給調用者

:: operator delete()會做相反的操作 - 移動指針, 訪問額外的數據,釋放內存。

而且通常在刪除分配到堆中的對象數組時使用[]。正如我所知,new []還會在分配內存的開始處添加額外的數據,其中存儲有關delete []運算符的數組大小的信息。它也可能是useful

換句話說,在一般的情況下的存儲器塊由分配新[]具有兩組的額外字節的實際數據的前面:以字節爲單位的塊的大小(由malloc的引入)和元素數量(由new []引入)。如你的例子所示,第二個是可選的。第一個通常總是存在的,因爲它是由malloc無條件分配的。即即使您只請求20,您的malloc調用也會在物理上分配超過20個字節。malloc將使用這些額外的字節來存儲塊大小(以字節爲單位)。通過要求 ...

「額外字節」新的[]從運營商新的[]不習慣「存儲分配的內存大小」,你似乎認爲。它們用於存儲數組中元素的數量,以便delete []知道要調用多少個析構函數。在你的例子中,析構函數是微不足道的。沒有必要打電話給他們。所以,不需要分配這些額外的字節並存儲元素數量。

1

它分兩個階段工作。一個是編譯器做的事情看起來很神奇,然後堆做的事情看起來很神奇。

也就是說,直到你意識到這個伎倆。然後魔法消失了。

但拳頭讓我們回顧當你做new X[12]時會發生什麼;

編譯器的掩護下,從概念上寫的代碼看起來是這樣的:

void* data = malloc(12 * sizeof(X)) 
for (int i=0; i != 12; ++i) { 
    X::Ctor(data); 
    data += sizeof(X); 
} 

哪裏Ctor(void* this_ptr)是設定this指針祕密函數調用X的構造在這種情況下,默認的。

因此,在破壞,我們可以撤消這一點,如果我們能藏在某處12不好找......

我猜你已經猜到了這裏已經...

隨地!真!例如,它可以在對象開始之前存儲。

第一行變得這3行:

void* data = malloc((12 * sizeof(X)) +sizeof(int)); 
*((int*)data) = 12; 
data += sizeof(int); 

其餘保持相同。

當編譯器看到delete [] addr它知道4個字節之前addr它可以找到對象計數。它還需要撥打free(addr - sizeof(int));

這實際上是與mallocfree使用的相同的技巧。至少在我們擁有簡單分配器的前幾天。