Speicher wird nicht mehr freigegeben ?!?!



  • Nuja, dein Urspruengliches programm hat ja folgende Variablen:

    char buffer[BUFFER_SIZE+1];
    DataList datalist[LIST_COUNT];
    

    Und verbraucht demnentsprechend deen folgenden Platz:

    BUFFERSIZE+1 //(fuer den buffer)
    + LIST_COUNT * sizeof(DataList)
    + Reservierter Speicher fuer alle vectoren
    

    mit dem swap/trick kriegst du den Verbrauch vom reservierten Speicher weg, aber die vector-"Ruempfe" (vermutlich ne Handvoll Pointer und n paar size_t's die speichern wie voll der vector ist) bleiben (und vom Speicherverbracuh ist ein DataList nicht mehr als ein vector). Mit der delete-version reduzierst du den Verbrauch auf

    BUFFERSIZE+1
    + LIST_COUNT * sizeof(DataList*)
    

    Wobei die datalist-pointer weniger Platz verbrauchen duerften als die vectoren.



  • ups.. Vesehentlich geschrieben... Aber danke Pumuckl für vector<Data>(datalist).swap(dataList); ! 🙂 👍



  • meine neuen erkenntnnisse mit der swap-clear-lösung 😃

    1. vector wird normal angelegt (stack) und nie gelöscht (weil kein blockende kommt, swap-clear funktioniert trotzdem (programm hat am ende 1,5MB) 👍
    2. vector wird mit new angelegt (heap) und nicht mit delete gelöscht, swap-clear funktioniert nicht!!!! (programm hat am ende 50MB) 👎
    3. vector wird mit new angelegt (heap) und mit delete am gelöscht, swap-clear funktioniert (programm hat am ende 1,5MB) 👎 👍

    fazit: möchte man den vector weiterverwenden, dann muss man version 1 verwenden, weil sonst wird der speicher erst irgendwann freigegben??



  • Hi,

    ich hinterfrage einfach mal die implizite Ausgangsthese, dass taff wirklich sehen kann, wann welcher Speicher "freigegeben" wird.
    Wenn man sich nicht selbst einen komplette Speicherverwaltung schreibt, sondern nur "vom Betriebssystem aus" auf den Prozess schaut, sieht man das Ganze durch eine viel zu dicke Decke.
    Dass eine Anwendung Speicher "deleted" bedeutet noch lange nicht, dass die Runtimelib dieses auch direkt dem Betriebssystem mitteilt.
    Und:
    Dass eine Runtimelib das Betriebssystem darüber informiert, dass es bestimmte Speicherblöcke nicht mehr braucht, bedeutet noch lange nicht, dass dieses sie für andere Anwendungen zur Verfügung stellt oder ein bestimmtes Anwenderprogramm (ps oder sonstwas) dieses als "frei" entdeckt.

    Deswegen sind so Aussagen wie "genau dann wird dieses freigegeben und jenes nicht" mit seeeehr viel Vorsicht zu genießen. Man kann einfach sehr viel falsch machen bei der Analyse der Speicherverwaltung...

    Gruß,

    Simon2.



  • @simon2
    sollte es nicht so sein unter linux, dass wenn man ein objekt "zerstört" auch wirklich freigegeben wird? so viel ich weiß hat linux keinen garbage collector.
    bzw. sieht man ja anhand meinem kleinem fazit, dass speicher ja durch tricks freigegeben wird und das sehe ich im top dann sofort oder in div. fällen nicht.

    @pumuckl
    wie würdest du dann die swap-clear-speicherbereinigung machen, wenn du angenommen 1000 elemente in der klasse hast und davon 500 löscht - beim normalen erase sollten ja nichts freigegeben werden 👎



  • taff schrieb:

    @pumuckl
    wie würdest du dann die swap-clear-speicherbereinigung machen, wenn du angenommen 1000 elemente in der klasse hast und davon 500 löscht - beim normalen erase sollten ja nichts freigegeben werden 👎

    So wie ichs bei Badestrands Frage vorgeschlagen hab:

    void DataList::ReservedToMinimum() {
       vector<Data>(dataList).swap(dataList);
    }
    

    Wenn du nach deinen ganzen erase()s, die ja nur das Objekt loeschen, aber nicht den reservierten Speicher freigeben, ReservedToMinimum() ausfuerhst, endest du mit einem vector der den minimalen Platz reserviert hat, der fuer den inhalt moeglich ist.



  • sorry, da war ich wohl mit den gedanken nur bei meinen sachen. 🙄

    aber eine sache habe ich bei der ganzen speicherreservierungspolitik von vectoren doch noch nicht durchschaut, weil wenn ich nun das ganze mit einem

    vector<Data*> dataList;
    

    mache, dann müsste die speicher belegung ja sich nur auf die größe des pointers beschränken und nicht auf den inhalt des pointers, oder?
    jedenfalls bleiben mir nun wieder 50mb im memory stehen 😞

    class Data 
    {
    	public:
    		string name;
    
    	public:
    		Data() {}
    		virtual ~Data() {}
    		void Set(const char* _name) {name = _name;}
    };
    
    class DataListPointer
    {
    	private:
    		 vector<Data*> dataList;	
    
    	public:
    		DataListPointer() {}
    		virtual ~DataListPointer() { Clear();}
    
    		void Clear()
    		{
    			cout << "DataListPointer::Clear()" << endl;
    			for(uint i=0; i< dataList.size(); i++)
    			{
    				delete dataList.at(i);
    				dataList.at(i) = NULL;
    			}
    			dataList.clear();
    			vector<Data*>().swap(dataList);
    		}
    
    		void Add(const char *_name)
    		{
    			Data *dataobj(new Data());
    
    			// Name setzen ----
    			if (_name) dataobj->Set(_name);
    
    			// Element in Liste hinzfügen
    			dataList.push_back (dataobj);
    		}
    };
    
    int main ()
    {
    	char buffer[BUFFER_SIZE+1];
    	memset(buffer, 'A', BUFFER_SIZE);
    	buffer[BUFFER_SIZE] = '\0';
    
    	DataListPointer *datalist[LIST_COUNT];
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx] = new DataListPointer();
    		for(int i=0; i<BUFFER_ADDS; i++)
    			datalist[idx]->Add(buffer);
    	}
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx]->Clear();
    		delete datalist[idx];
    		datalist[idx] = NULL;
    	}
    
    	while(1)
    		sleep(1);
    
    }
    

    laut valgrind ist auch nichts belegt:
    ==31907== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from 1)
    ==31907== malloc/free: in use at exit: 0 bytes in 0 blocks.
    ==31907== malloc/free: 2,050 allocs, 2,050 frees, 106,498,300 bytes allocated.

    ich versteh die c++ welt nicht mehr 😞



  • taff schrieb:

    @simon2
    sollte es nicht so sein unter linux, dass wenn man ein objekt "zerstört" auch wirklich freigegeben wird? so viel ich weiß hat linux keinen garbage collector...

    Also Linux hat definitiv einen "Garbage Collector" (= eine Instanz, die Ressourcen aufräumt, die ein Programm "fallen lässt"). In C++ ist aber kein garbage collector für die Runtime vorge- oder beschrieben.

    Unter "wirklich freigeben" versteht der Standard eben nur: "Das Betriebssystem darüber informieren, dass dieser Speicher nicht mehr benötigt wird". Nirgendwo wird festgelegt, was das Betriebssystem ("OS") mit dem Speicher anzustellen hat - und das ist auch gut so.
    Deswegen kann das OS z.B. erstmal abwarten, ob das Programm nicht gleich wieder genausoviel Speicher wieder allozieren will. Damit spart es "Performance", weil der Speicher nicht erst dem "globalen Pool" bekanntgemacht werden muss UND weil der Speicher weniger fragmentiert wird. Oder ... oder ... oder ....
    Dahinter stehen sehr komplexe Strategien.

    taff schrieb:

    @simon2
    ...sieht man ja anhand meinem kleinem fazit, dass speicher ja durch tricks freigegeben wird und das sehe ich im top dann sofort oder in div. fällen nicht....

    Japp - aber WANN das der Fall ist, kannst Du nicht erzwingen, sondern liegt im Belieben des OS (außer, wenn Du einen Programmierfehler machst und den Speicher nicht freigibst).

    Genau DAS beobachtest Du auch hiermit:

    taff schrieb:

    ...
    jedenfalls bleiben mir nun wieder 50mb im memory stehen 😞
    ...
    laut valgrind ist auch nichts belegt:
    ==31907== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from 1)
    ==31907== malloc/free: in use at exit: 0 bytes in 0 blocks.
    ==31907== malloc/free: 2,050 allocs, 2,050 frees, 106,498,300 bytes allocated.

    ich versteh die c++ welt nicht mehr 😞

    ... ich würde sagen: Du verstehst die Welt von OS-Speichermanagement nicht. 😉
    Valgrind sagt Dir, dass Dein Programm alles richtig gemacht hat, das sollte Dir reichen.

    Mal einfach anders herum (Annahme: Du schreibst ein OS): Warum sollte ein OS Zeit darin verschwenden, 50 MB wieder in den Pool zu werfen, wenn es noch 1,85 GB Speicher für andere Programme frei hat ?
    IIRC hat selbst das 70er-Jahre-Unix Speicher erst dann wieder für andere Prozesse freigegeben, wenn der Gesamtspeicherbedarf knapp wurde...

    Gruß,

    Simon2.



  • Ich würde im Code noch etwas abkürzen

    void Clear()
            {
                cout << "DataListPointer::Clear()" << endl;
                for(uint i=0; i< dataList.size(); i++)
                {
                    delete dataList.at(i);
                }
                vector<Data*>().swap(dataList);
            }
    
            void Add(const char *_name)
            {
                // Element in Liste hinzfügen
                dataList.push_back (new Data());
                // Name setzen ----
                if (_name) dataList.back()->Set(_name);
            }
    


  • Simon2 schrieb:

    Unter "wirklich freigeben" versteht der Standard eben nur: "Das Betriebssystem darüber informieren, dass dieser Speicher nicht mehr benötigt wird". Nirgendwo wird festgelegt, was das Betriebssystem ("OS") mit dem Speicher anzustellen hat - und das ist auch gut so.
    Deswegen kann das OS z.B. erstmal abwarten, ob das Programm nicht gleich wieder genausoviel Speicher wieder allozieren will. Damit spart es "Performance", weil der Speicher nicht erst dem "globalen Pool" bekanntgemacht werden muss UND weil der Speicher weniger fragmentiert wird. Oder ... oder ... oder ....
    Dahinter stehen sehr komplexe Strategien.

    nur die frage ist, warum schaff ich eine lösung wo der speicher freigegeben wird

    vector<Data> dataList;
    

    und eine wo er nicht freigegeben wird?

    vector<Data*> dataList;
    

    Simon2 schrieb:

    ... ich würde sagen: Du verstehst die Welt von OS-Speichermanagement nicht. 😉
    Valgrind sagt Dir, dass Dein Programm alles richtig gemacht hat, das sollte Dir reichen.

    ähm das reicht leider eben nicht, weil dadurch habe ich einen zu hohen speicherverbrauch auf einem disklessserver, also so einfach ist die sache in meinen augen nicht, leider.

    ich habe nun noch was weiteres rausgefunden

    1. es muss ganz stark darauf an kommen wo (stack/heap) ich die vector-dataelemente anlege, warum auch immer?
    vector<Data> dataList;    bzw.     vector<Data*> dataList;
    
    1. so komisch es ausschaut, aber es ist auch ein unterschied wo ich den buffer anlege den ich ständig einem string zuweise, aber seht selbst:

    in der folgenden version bleiben am ende bei mir 50MB über, obwohl alles richtig freigegeben wurde!!

    int main ()
    {
    	char buffer[BUFFER_SIZE+1];
    	memset(buffer, 'A', BUFFER_SIZE);
    	buffer[BUFFER_SIZE] = '\0';
    
    	DataListPointer *datalist[LIST_COUNT];
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx] = new DataListPointer();
    		for(int i=0; i<BUFFER_ADDS; i++)
    			datalist[idx]->Add(buffer);
    	}
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx]->Clear();
    		delete datalist[idx];
    		datalist[idx] = NULL;
    	}
    
    	while(1)
    		sleep(1);
    
    }
    

    in dieser version wird auch noch der buffer per new angelegt und der speicher wird freigegeben!!!!!!

    int main ()
    {
    	char *buffer(new char[BUFFER_SIZE+1]);
    	memset(buffer, 'A', BUFFER_SIZE);
    	buffer[BUFFER_SIZE] = '\0';
    
    	DataListPointer *datalist[LIST_COUNT];
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx] = new DataListPointer();
    		for(int i=0; i<BUFFER_ADDS; i++)
    			datalist[idx]->Add(buffer);
    	}
    
    	for(int idx=0; idx<LIST_COUNT; idx++)
    	{
    		datalist[idx]->Clear();
    		delete datalist[idx];
    		datalist[idx] = NULL;
    	}
    
    	delete buffer;
    
    	while(1)
    		sleep(1);
    
    }
    

    merkwürdig oder? 😕



  • Objekte die hinter einem Pointer versteckt sind bzw. auf dem Heap angelegt sind, muß man immer selber freigeben. Da ist nichts komisches dran. ➡ By-Design!

    Wenn du Polymorphie in einem Container haben willst, kannst du auch Smartpointer benutzen. Dann sorgt der Smartpointer dafür, aufzuräumen.

    Und du brauchst uns auch nichts zeigen, weil wir das alles schon selber wissen. Der Unterschied zu Dir und uns ist: wir haben es verstanden, du nicht! 😉 Deshalb finden wir es nicht merkwürdig. Weil ➡ By-Design!

    Wo ist also jetzt dein Problem?



  • Artchi schrieb:

    Objekte die hinter einem Pointer versteckt sind bzw. auf dem Heap angelegt sind, muß man immer selber freigeben. Da ist nichts komisches dran. ➡ By-Design!

    MOMENT, wo gebe ich irgendwo keinen speicher frei? er wird überall freigegeben! nur laut top bleibt trotzdem speicher belegt!

    Artchi schrieb:

    Wenn du Polymorphie in einem Container haben willst, kannst du auch Smartpointer benutzen. Dann sorgt der Smartpointer dafür, aufzuräumen.

    verwende ich einen smartpointer bleiben mir trotzdem 50MB im speicher, obwohl laut valgrind alle objekte zerstört wurden!

    Artchi schrieb:

    Wo ist also jetzt dein Problem?

    das problem liegt darin, dass eben bei ALLEN designs der speicher IMMER von mir freigegeben wird und trotzdem hat dies bei div. designs keine auswirkung (laut top)



  • dann solltest du mal lernen die ausgaben von top und free zu interpretieren,

    #free :
    Mem: (total)2596496 (used)2280108 (free)316388
    /+ buffers/cache: (used)724380 (free)1872116

    will sagen, das dir top einfach sagt, für wieviel speicher linux grade keine
    Verwendung hat, und nicht wieviel wirklich frei ist,
    bei der einen implementierung findet linux halt deinen speicher cache-wert, und
    bei der anderen nicht, das sagt aber nicht, das du mehr speicher verbrauchst

    das hat mit c++ nichts, aber auch wirklich garnichts zu tun, oder willst du
    linux noch vorschreiben, wann es cachen//buffern darf, und wann nicht?

    nimm free und sieh dir die werte ohne buffers//caches an !!!



  • Treb schrieb:

    dann solltest du mal lernen die ausgaben von top und free zu interpretieren,

    #free :
    Mem: (total)2596496 (used)2280108 (free)316388
    /+ buffers/cache: (used)724380 (free)1872116

    ich glaube die werte die bei der jeweiligen PID bei VIRT und RES stehen haben hier schon so manche aussagekraft in meinen augen. was unter used und free steht kann für solche messungen nicht direkt verwendet werden.

    Treb schrieb:

    das hat mit c++ nichts, aber auch wirklich garnichts zu tun, oder willst du linux noch vorschreiben, wann es cachen//buffern darf, und wann nicht?

    nein tu ich doch gar nicht, aber warum verhalten sich dann IMMER div. designs gleich? warum habe ich bei design A am ende nur 1,5MB speicher verbraucht und bei design B 50MB speicherverbrauch obwohl IMMER alle objekte brav zerstört wurden? ich habe null speicherleichen und trotzdem sind bei diesem design 50MB verbraucht.
    warum bleibt nun bei design B.1 wenn ich statt dem buffer der sonst immer im stack angelegt wurde und nun im heap am ende wieder nur noch 1,5MB über und nicht wie vorher 50MB und das obwohl bei beiden designs B und B.1 alle objekte zersötrt wurden. 😕



  • taff schrieb:

    ... nur die frage ist, warum schaff ich eine lösung wo der speicher freigegeben wird

    vector<Data> dataList;
    

    und eine wo er nicht freigegeben wird?

    vector<Data*> dataList;
    

    Ganz einfach: Zufall !
    Das ist genauso, wie es durchaus üblich ist, dass Lesezugriffe auf bereits freigegebenen Speicher noch erfolgreich sind (sprich: Das gewünschte Ergebnis liefern). Aber da bewegst Du Dich über die Zusagen des Standards hinaus und mit einem neu compilierten Kernel, anderen Startoption, .... klappt das vielleicht auch nicht mehr.

    taff schrieb:

    ...

    Simon2 schrieb:

    ... ich würde sagen: Du verstehst die Welt von OS-Speichermanagement nicht. 😉
    Valgrind sagt Dir, dass Dein Programm alles richtig gemacht hat, das sollte Dir reichen.

    ähm das reicht leider eben nicht, weil dadurch habe ich einen zu hohen speicherverbrauch auf einem disklessserver, also so einfach ist die sache in meinen augen nicht, leider....

    Dann solltest Dich aber "an Dein OS wenden". Das sind Probleme, die Du mit Standard-C++ nicht mehr lösen kannst.

    Und dazu: Was ist denn "ein zu hoher Speicherverbrauch" ? Bekommt irgendein Programm keinen Speicher mehr ? Oder hättest Du einfach gerne noch ein paar MB frei ? ...

    Gruß,

    Simon2.



  • Simon2 schrieb:

    Ganz einfach: Zufall !
    Das ist genauso, wie es durchaus üblich ist, dass Lesezugriffe auf bereits freigegebenen Speicher noch erfolgreich sind (sprich: Das gewünschte Ergebnis liefern). Aber da bewegst Du Dich über die Zusagen des Standards hinaus und mit einem neu compilierten Kernel, anderen Startoption, .... klappt das vielleicht auch nicht mehr.

    aber der zufall ist für mich ein sehr schelchter zufall. ich habe das ganze nun auf 3 verschiedenen kernels probiert und überall das gleiche ergebnis bekommen. was mich jedoch am meisten wundert, dass ist die sache mit dem buffer den ich übergebe, damit ich den speicher fülle, dass sogar ein buffer der einmal angelegt wird und x-mal übergeben wird, das ganze beeinflussen kann ob am ende der speicher freigegeben wird oder nicht?! wie berichtet macht es einen unterschied aus ob man diesem im stack oder heap anlegt, obwohl dieser nur einmal angelegt wird?!

    Simon2 schrieb:

    Dann solltest Dich aber "an Dein OS wenden". Das sind Probleme, die Du mit Standard-C++ nicht mehr lösen kannst.

    Und dazu: Was ist denn "ein zu hoher Speicherverbrauch" ? Bekommt irgendein Programm keinen Speicher mehr ? Oder hättest Du einfach gerne noch ein paar MB frei ? ...

    Beides, weil wenn dann unsere techniker diskless-server planen, dann kommt es darauf an, wieviel speicher die dienste so verbrauchen und da eben bei so diskless servern nichts auf eine hdd gecached werden kann, hat man eben nur den speicher zuverfügung und somit soll das programm nur den speicher belegen den es auch wirklich braucht.

    was mir auch noch aufgefallen ist, dass wenn ich mein testprogramm laufen lasse und ich starte einfach ein anderes programm welches nur den speicher frist und das ziel hat den speicher anzufüllen bis nichts mehr geht, dann wird der speicher von meinem testprogramm, welcher eigentlich nicht benötigt wird auch NICHT freigegeben, damit ein anderer dienst diesen verwenden könnte.



  • Na dann ist dein Problem doch gelöst! Es wurde doch vorher schon im Thread beschrieben, daß das OS solange cached bis ein anderer Prozeß den Speicher braucht.



  • Ups, war doppelt



  • Tyrdal schrieb:

    Na dann ist dein Problem doch gelöst! Es wurde doch vorher schon im Thread beschrieben, daß das OS solange cached bis ein anderer Prozeß den Speicher braucht.

    wenn es so wäre, wäre es ja schön, leider wie berichtet:

    taff schrieb:

    dann wird der speicher von meinem testprogramm, welcher eigentlich nicht benötigt wird auch NICHT freigegeben, damit ein anderer dienst diesen verwenden könnte.



  • Doch, du hast doch selbst die valgrind-Ausgabe gepostet. Das was du als freien Speicher ansiehst ist aus OS-Sicht vergeudeter Speicher. Wenn ihn keine anderer Prozeß braucht wird er z.B. zwecks Performanceverbesserung für Caches genutzt. Wenn dann ein Prozeß trotzdem soviel Speicher braucht, daß der "freie" nicht ausreicht wird eben so ein Cache verkleinert o.ä.. Ergo, wenn du den Speicher in deinem Programm korrekt freigibst steht er anderen Programmen auch zu Verfügung.


Anmelden zum Antworten