Speicher wird nicht mehr freigegeben ?!?!



  • 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.



  • aber laut top sind dann diesem dienst trotzdem noch immer 50MB zugewiesen und die werden dann trotzdem nicht abgegeben, wenn ein anderer dienst mehr speicher benötigen würde als was noch vorhanden ist.



  • Siehe mein Edit oben.



  • wie weiter oben berichtet, wollte ich das überprüfen ob dann dieser vergeudete speicher freigegeben wird. ich hab mir ein programm gebastelt, welches einfach nur speicher reserviert bis es nicht mehr geht. somit müssten eigentlich auch die 50mb welche korrekt freigegeben wurden, diesem speicher-fress-testprogramm zur verfügung gestellt werden. leider ist das nicht so. die 50mb werden diesem speicher-fress-testprogramm nicht zur verfügung gestellt.



  • Wie genau hast du das denn getestet? Gesamthauptspeicher - Kernel sollen allokiert werden, oder wie soll man das verstehen?



  • auf einem diskless server (also caching auf hdd nicht möglich)

    1. dann habe ich mein testprogramm gestartet wo der speicher nicht mehr freigegeben wird am ende.
    2. von meinem testprogramm eine andere version gebastelt, wo ich eben unendlich viele objekte anlege.

    ich konnte schön sehen wie der used mehr wurde und der free weniger 😃 bzw. auch der cached am ende weniger wurde, jedoch änderte sich nichts beim speicherverbrauch bei meinem programm, welches eigentlich den speicher freigegeben hat.



  • taff schrieb:

    wie weiter oben berichtet, wollte ich das überprüfen ob dann dieser vergeudete speicher freigegeben wird. ich hab mir ein programm gebastelt, welches einfach nur speicher reserviert bis es nicht mehr geht. somit müssten eigentlich auch die 50mb welche korrekt freigegeben wurden, diesem speicher-fress-testprogramm zur verfügung gestellt werden. leider ist das nicht so. die 50mb werden diesem speicher-fress-testprogramm nicht zur verfügung gestellt.

    Also wenn das wirklich so ist, dann ist irgendwas an Deinem OS oder Deiner C++-Runtimelib kaputt/schräg eingestellt.
    Was mich noch ein wenig interessiert:

    taff schrieb:

    meine neuen erkenntnnisse mit der swap-clear-lösung 😃
    ...
    2) vector wird mit new angelegt (heap) und nicht mit delete gelöscht, swap-clear funktioniert nicht!!!! (programm hat am ende 50MB) 👎
    ...

    Wie machst Du das genau ? Sowas in der Art ?

    vector<*int> v;
    for(i=0; i<=max; ++i) v.push_back(new int);
    

    ??

    Dann wäre es vollkommen klar, weil vector<T*> kein delete aufruft => Ergo: Selbstgebasteltes Speicherleck.
    Der "Swap-clear-Trick" ist für eine andere Situation gedacht. Er ist nicht dazu da, deletes zu ersetzen.

    Übrigens: Ich würde unter "... vector mit new angelegt ..." sowas verstehen:

    vector<int> *v = new vector<int>;
    for(i=0; i<=max; ++i) v->push_back(1);
    

    Aber auch hier braucht's ein abschließendes delete....

    EDIT: Aber beides hätte valgrind bestimmt gefunden !!

    Gruß,

    Simon2.



  • muss gestehen, dass ich den 2. punkt von damals nicht mehr so hinbekomme mit diesem fehler, liegen doch schon zu viele versionen dazwischen 😡 , jedoch weiß ich hier nun wie ich mir abhelfen kann, wenn das vector-daten-element im stack angelegt wird.

    anders sieht es aus, wenn dies im heap liegt, man kann auch einen smart_ptr verwenden, gleiches problem:

    #define LIST_COUNT 5
    #define BUFFER_SIZE 1024
    #define BUFFER_ADDS 10000
    
    class Data 
    {
    	public:
    		string name;
    
    	public:
    		Data() {}
    		~Data() {}
    		void Set(const char* _name) {name = _name;}
    };
    
    class DataListPointer
    {
    	private:
    		 vector<Data*> dataList;	
    
    	public:
    		DataListPointer() {}
    		virtual ~DataListPointer() { }
    
    		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); // kann man aufrufen oder weglassen, hat am ende keinen einfluss 
    		}
    
    		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);
    
    }
    

    lege ich aber den BUFFER im heap an:

    char *buffer = new char[BUFFER_SIZE+1];
    

    dann wird komischer weise beim delete des buffers wieder alles freigegeben, also auch die uminösen 50MB.

    verwende ich für den buffer einen stl-string

    string buffer;
    buffer.append(BUFFER_SIZE+1, 'A');
    

    werden am ende auch die 50MB nicht freigegeben!

    ich wäre froh, wenn das obige beispiel wer testhalber auf seiner maschine laufen lassen könnte, weil ich komm mir schon vor als würde ich sachen erzählen die mir niemand glaubt 😞 🙄 BITTE - DANKE


Anmelden zum Antworten