2016-07-05 6 views
6

Ich schrieb ein kleines Programm, das std :: verwendet WarteschlangeWarum verkleinert std :: queue seinen Speicher nicht nach dem Aufrufen von Elementen?

queue<double> the_queue; 

for(int i = 0; i < 100; i++) 
{ 
    for(int j = 0; j < 5000; j++) 
    { 
     double d = 1.0 * (rand() % 1000); 
     the_queue.push(d); 
    } 
} 

printf("Done pushing\n"); 

while(!the_queue.empty()) 
{ 
    the_queue.pop(); 
} 

printf("Done popping\n"); 

Ich habe 2 Stützpunkte an printf("Done pushing\n"); und printf("Done popping\n"); und Speichernutzung des Prüfprogramm (zeigte im Task-Manager), wenn die Haltepunkte getroffen werden. Bei Done pushing ist die Speicherauslastung ~ 34 MB, aber bei Done popping ist die Speicherauslastung immer noch ~ 34 MB. Das überrascht mich!

Warum ist das? Gibt es einen Weg, dies zu überwinden?

+0

Es gibt auch die ganze Sache, dass, selbst wenn Sie den Speicher als frei an die C++ - (und C) Laufzeit zurückgeben, wird diese Laufzeit wahrscheinlich nicht direkt zurück zum Betriebssystem, so wird das Programm konsumiere diesen Speicher immer noch, soweit es das Betriebssystem betrifft. – Angew

Antwort

7

Grundsätzlich std::queue ist ein Adapter Container - es ist kein eigener Container, sondern eine dünne Verpackung um andere Container.

Zum Beispiel können Sie einen Blick auf der Warteschlange Unterschrift geleistet werden:

template <class T, class Container = deque<T> > class queue; 

wie Sie sehen können, T ist der Typ des in der Warteschlange gespeichert Elements und Container sind die zugrunde liegenden Behälter.

und das ist die Antwort auf Ihre Frage: verschiedene Container behandelt Speicher anders. die zugrunde liegende deque kann schrumpfen oder nicht, aber es liegt an der innenseite zu entscheiden.

können Sie auch std::list als zugrunde liegenden Container verwenden. In diesem Fall löscht jeder Popup den zugrunde liegenden Listenknotenspeicher.

Sie können auch eigene Dateien schreiben oder vorhandene Container ändern, um sie an Ihre eigenen Speicherverwaltungsmuster anzupassen. Ihr Container muss einige Methoden unterstützen (suck as push_back, pop_front), die Sie in der entsprechenden Online-Dokumentation lesen können. Hier

ist ein Beispiel an einen deque Adapter, der alle 1024 pop Anrufe in Kapazität schrumpft:

template<class T> 
class DequeAdapater{ 

private: 
    std::deque<T> m_Deque; 
    size_t m_PopCount; 

public: 
    DequeAdapater(): 
     m_PopCount(0){} 

    bool empty() const noexcept{ 
     return m_Deque.empty(); 
    } 

    size_t size() const noexcept{ 
     return m_Deque.size(); 
    } 

    T& front() noexcept{ 
     return m_Deque.front(); 
    } 

    const T& front()const noexcept{ 
     return m_Deque.front(); 
    } 

    T& back() noexcept{ 
     return m_Deque.back(); 
    } 

    const T& back()const noexcept{ 
     return m_Deque.back(); 
    } 

    void push_back(const T& t){ 
     return m_Deque.push_back(t); 
    } 

    void push_back(T&& t){ 
     return m_Deque.push_back(std::move(t)); 
    } 

    void pop_front(){ 
     m_Deque.pop_front(); 
     m_PopCount++; 
     if (m_PopCount%1024U == 0U){ 
      m_Deque.shrink_to_fit(); 
     } 
    } 

} 


template <class T> 
using LeanQueue = std::queue<T,DequeAdapater<T>>; 

Anmerkung jedoch nicht, dass in der Kapazität schrumpfen bedeutet Verschieben oder Kopieren der Warteschlangenelemente auf den neuen mageren Brocken, die Der Speicherverbrauch ist geringer, aber die Leistung kann sich verschlechtern.

0

Da pop nur seine Größe verringert, ist nicht die Kapazität.

Dann, wenn Sie andere Elemente in die Warteschlange legen, muss es nicht Speicher reservieren, es wird nur die bereits zugewiesene verwenden.

+0

Ich habe eine ähnliche Sache in einem kommerziellen Programm implementiert und die ähnliche Situation ist aufgetreten. Mein Chef fragte mich, warum das Programm zu viel Speicher verbraucht. Kann ich etwas tun? –

+0

Vielleicht ändern STL-Container? –

+2

@duong_dajgja vielleicht 'std :: queue () .swap (the_queue);' –

5

Jeder Speicher, den die Warteschlange verwaltet, wird freigegeben, wenn die Warteschlange den Bereich verlässt.

Aber auch dann wird der Speicher möglicherweise nicht auf das Betriebssystem zurückgesetzt, da die Standardbibliothek davon ausgeht, dass Sie den Speicher möglicherweise wieder benötigen, wenn Sie den Speicher zuvor verwendet haben.

Die Besonderheiten werden in malloc/free in der spezifischen c-Laufzeitbibliothek behandelt, gegen die Ihr Programm gebunden ist.

Ist dies ein eingebettetes System, in dem der Speicher knapp ist? (in diesem Fall vielleicht Container mit fester Größe betrachten), oder läuft es auf einem Server/Desktop/iPad? (In diesem Fall sagen Sie Ihrem Chef, dass er aufhören soll, sich über Dinge zu sorgen, die er nicht versteht).

0

Das Zuweisen und Freigeben von Speicher kann mit einem Container, der regelmäßig wieder und wieder verwendet wird, zu einem hohen Aufwand werden. Aus diesem Grund dürfen Behälter in dem Maße wachsen, in dem sie verwendet werden. Wenn Sie den Speicher zurückfordern wollen, müssen Sie dies explizit tun. Oft ging es darum, dem Container zu erlauben, den Gültigkeitsbereich zu verlassen, da der Standard (aus irgendeinem Grund) keine Funktion zur Freigabe des Speichers bietet. Es gibt eine Funktion, um möglicherweise nicht verwendeten Speicher auf einigen Containern freizugeben (shrink_to_fit()), aber es ist keine Garantie.

Es ist glücklicherweise ein Idiom, das einen Behälter seine Ausgangsbetrag Speicher genannt Swap- und zurückgesetzt zu reduzieren, verwendet wird.

Sie erstellen grundsätzlich einen neuen leeren Container und swap() seinen (leeren) Inhalt mit Ihrem Arbeitscontainer.

// create a temporary empty container and swap its 
// contents with the working container 
std::queue<double>().swap(q);