2016-07-07 17 views
5

Wir haben einige Speicherprobleme, wenn std::vector ein Feld einer Klasse ist. Wir füllen diesen Vektor mit vielen Daten, die an einem bestimmten Punkt des Programms freigegeben werden müssen. Obwohl die Vektorkapazität Null ist, wird der Speicher jedoch nicht freigegeben oder vollständig freigegeben.Speicher wurde beim Leeren von std :: vector in einer benutzerdefinierten Klasse nicht freigegeben

Hier haben Sie eine vereinfachte Version unseres Programms. Wie Sie sehen können, hat die Klasse Foo nur ein Feld: a std::vector<int>. Wenn wir einen std::vector<Foo> erstellen und ihn mit Foo Objekten füllen, wird der Speicher nicht vollständig freigegeben, wenn wir den Vektor innerhalb jedes Objekts leeren.

Wir haben die Speicherbelegung mithilfe des Aktivitätsmonitors gemessen und Sie können neben jeder Protokollzeile die Anzahl der in jeder Phase verwendeten Bytes sehen. Außerdem haben wir eine weitere Version hinzugefügt, in der wir keine Objekte der Klasse Foo verwenden, und in diesem Fall wird der Speicher perfekt freigegeben.

#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; 
} 

Wenn es hilft, verwenden wir g ++ 4.8.4 unter Ubuntu 14.04 64-Bit. Die spezifische Speicherbelegung hängt davon ab, ob wir C++ 11 oder C++ 98 verwenden, aber das gleiche Phänomen tritt in beiden Fällen auf.

Irgendwelche Ideen, was passiert und wie diese Erinnerung gewaltsam wiederhergestellt werden kann, wenn nötig?

EDIT: Beachten Sie, dass Speicher meist in der Tat zurückgegeben wird, wenn wir alle Objekte der Klasse Foo zerstören, aber in unserer realen Welt Problem müssen wir noch den Rest des Inhalts der Foo -Analogon Klasse.

+6

Wie haben Sie gemessen? Gewöhnlich wird jeder Speicher, der vom Prozess vom Betriebssystem erworben wurde, nicht _ zurückgewiesen _, wenn der Speicher intern freigegeben wird. –

+1

Es ist ein Teil des Lebens, fürchte ich. Aber verursacht es tatsächlich ein Problem? Moderne Betriebssysteme und C++ Laufzeiten sind gut darin, Speicher freizugeben, wenn sie es wirklich brauchen. – Bathsheba

+0

Das Freigeben von Speicher in einer Anwendung muss nicht den gesamten Speicher zurück an das Betriebssystem freigeben, da die Zuordnungen auf der Betriebssystemebene wahrscheinlich größere Abschnitte sind. – drescherjm

Antwort

3

@ user1641854 Antwort ist richtig, warum dies passiert. In dieser Antwort geht es darum, es zu beheben.

Es gibt eine relativ einfache Möglichkeit, Ihr Problem zu beheben. Sie können Ihrem Vektor einen allocator geben, der intern direkt das Betriebssystem nach Speicher fragt und es direkt an das Betriebssystem freigibt, wenn es frei ist. Dies ist im Allgemeinen unerwünscht, da direkt von der OS-Zuweisung im Allgemeinen langsamer zuzuteilen/frei ist als ein gut gestalteter Usermode-Heap und Sie am Ende der Seiten etwas Speicher verschwenden. Ohne etwas Aufwand wäre es auch nicht plattformübergreifend.

Nachdem gesagt, dass Ihr Fall scheint einer, wo dies eine vernünftige Sache zu versuchen wäre.

Weitere Informationen zum Definieren eines eigenen Zuordners finden Sie unter here.

Dann verwenden Sie ::VirtualAlloc/::VirtualFree auf Windows oder mmap/munmap auf Linux für die zugrunde liegenden Zuteilung/freie Funktionen.

10

Speicher wird von C++/C-Laufzeitbibliotheken für Benutzerspeicherzugriff freigegeben. Im Allgemeinen bedeutet dies nicht, dass der Benutzerbereichszuordner diesen Speicher an OS zurückgibt. User Space Allocator weist Speicher von Kernel für Blöcke zu. Diese Blöcke werden weiter auf Ihre Anfragen durch neue/malloc geschnitten. Wenn Sie diese geschnittenen Blöcke freigeben/löschen, werden sie an den Benutzerbereichszuordner und nicht an den Kernel zurückgegeben. Und wenn der User Space Allocator in der Lage sein wird, den zugewiesenen Speicherblock zum Kernel zurückzugeben, ist er nur durch den User Space Allocator bekannt.

+0

Das kann der Fall sein, aber die Problem ist, dass der Speicher, dass der User Space Allocator nicht zurückgegeben wurde, an Objekte gebunden ist, die unter der Klasse Foo erstellt wurden. In unserem realen Problem haben wir eine Klasse, die mehrere GB verwendet und sie zurückgeben sollte, so dass andere Klassen diesen Speicher wiederverwenden können, aber die anderen Klassen am Ende neue Speicherblöcke erhalten und wir den Swap-Speicher erreichen. Ich habe die Frage aktualisiert, um das Problem besser widerzuspiegeln. – rafapages

+0

@rafageseiten verwenden Sie irgendeine Art von Referenzzählung? – user1641854

+1

@ Rafapages in jedem Fall - Sie können Ihre Heap-Verbrauch z. Verwenden Sie Valgrind-Tool namens Massif. Sie werden verstehen können, was genau das Objekt Speicher nicht freigibt. Oder wenn valgrind massif kein Fall ist, können Sie Ihren eigenen Allokator schreiben, der alle Zuweisungen/Deallacations verfolgt. Es ist nicht sehr schwer. – user1641854