2016-07-07 47 views
5

std::vector是類的字段時,我們遇到了一些內存問題。我們在這個向量中填充了大量的數據,這些數據在程序的某個點需要發佈。但是,即使向量容量爲零,內存也不會釋放或完全釋放。當在用戶定義的類中清空std :: vector時釋放內存

在這裏,您有我們程序的簡化版本。如您所見,類Foo只有一個字段:a std::vector<int>。如果我們創建一個std::vector<Foo>並填充Foo對象,那麼當我們清空每個對象內部的矢量時,內存不會完全釋放。

我們使用活動監視器測量了內存使用情況,您可以在每個日誌行旁邊看到每個階段中使用的字節數。此外,我們增加了另一個版本,我們不使用類Foo對象,在這種情況下,內存被完美釋放。

#include <iostream> 
#include <vector> 

class Foo { 

public: 
    std::vector<int> container; 
}; 

int main() { 
    int n1 = 1000; 
    int n2 = 100000; 
    { 
     std::vector<Foo> foos; 

     std::cerr << "starting" << std::endl; // 160 KiB 
     std::cin.get(); 

     for (int i = 0; i < n1; i++){ 
      Foo foo; 
      foo.container.assign(n2, 666); 
      foos.push_back(foo); 
     } 

     std::cerr << "foos filled" << std::endl; // 382.1 MiB 
     std::cin.get(); 

     for (unsigned int i = 0; i < foos.size(); i++){ 
      std::vector<int>().swap(foos[i].container); 
     } 

     std::cerr << "foos emptied" << std::endl; // 195.7 MiB 
     std::cin.get(); 
    } 
    std::cerr << "foos destroyed?" << std::endl; // 296 KiB 
    std::cin.get(); 

    { 
     std::vector<std::vector<int> > foos; 

     std::cerr << "starting" << std::endl; // 296 KiB 
     std::cin.get(); 

     { 
      std::vector<int> aux; 
      aux.assign(n2, 666); 
      foos.assign(n1, aux); 
     } 

     std::cerr << "foos filled" << std::endl; // 382.1 MiB 
     std::cin.get(); 

     for (unsigned int i = 0; i < foos.size(); ++i) { 
      std::vector<int>().swap(foos[i]); 
     } 

     std::cerr << "foos emptied" << std::endl; // 708 KiB 
     std::cin.get(); 
    } 

    std::cerr << "foos destroyed?" << std::endl; // 708 KiB 
    std::cin.get(); 


    return 0; 
} 

如果有幫助,我們在Ubuntu 14.04 64位下使用g ++ 4.8.4。具體的內存佔用率取決於我們使用C++ 11還是C++ 98,但在兩種情況下都會出現相同的現象。

關於發生了什麼以及如何恢復該內存的任何想法,如果需要強制執行?

編輯:請注意,當我們銷燬Foo類的所有對象時,內存大部分會返回,但在我們現實世界的問題中,我們仍然需要Foo -analogue類的其餘內容。

+6

你是怎麼測量的?如果內存在內部被釋放,通常任何由OS進程獲得的內存都不會「返回」。 –

+1

恐怕是生活的一部分。但它確實會造成問題嗎?現代操作系統和C++運行時在需要時可以釋放內存。 – Bathsheba

+0

釋放應用程序中的內存不需要將所有內存釋放回操作系統,因爲操作系統級別的分配可能更大。 – drescherjm

回答

3

@ user1641854的答案正確,爲什麼會發生這種情況。這個答案是關於修復它。

有一個相對簡單的方法來解決您的問題。你可以給你的矢量一個allocator,它在內部直接詢問操作系統的內存,並在free'd時直接將它釋放回操作系統。這通常是不受歡迎的,因爲從操作系統分配的直接分配通常比設計良好的用戶模式堆分配要慢,並且在頁面結尾處會浪費一些內存。而且,沒有一些努力,它不會跨平臺。

話雖如此,您的情況似乎是一個合理的嘗試。

有關如何定義您自己的分配器的信息,請參見here

然後在windows上使用::VirtualAlloc/::VirtualFree或在Linux上使用mmap/munmap進行底層分配/釋放功能。

10

內存從運行時C++/C庫發佈到用戶空間內存分配器。一般來說,這並不意味着用戶空間分配器會將此內存返回給操作系統。用戶空間分配器通過塊從內核分配內存。這些塊進一步通過new/malloc在您的請求中被分割。當你釋放/刪除這些分片塊時,它們將返回給用戶空間分配器,而不是內核。而當用戶空間分配器將能夠將分配的內存塊返回給內核時,只有用戶空間分配器才知道。

+0

這可能是這種情況,但問題在於用戶空間分配器沒有返回的內存似乎與綁定到類Foo下創建的對象。在我們現實世界的問題中,我們有一個使用幾個GB的類,並且應該返回它們,以便其他類可以重用該內存,但其他類最終會接收到新的內存塊,並最終觸發交換內存。我已經更新了問題以更好地反映問題。 – rafapages

+0

@rafapages你使用任何類型的引用計數? – user1641854

+1

@rafapages在任何情況下 - 你可以分析你的堆消費,例如使用稱爲地塊的valgrind工具。你將能夠理解到底什麼對象沒有釋放內存。或者,如果valgrind地塊不是案例,您可以編寫自己的分配器,用於跟蹤所有分配/交易。這不是很難。 – user1641854