Element aus Vektor löschen und Speicher freigeben



  • @CStoll: Mit boost würde es wohl gehen, aber ich würde es gerne ohne hinbekommen. Wie muss ich die Objekte denn mit delete löschen? So wie ich es versucht habe (siehe oben) hat es ja nicht funktioniert.

    @TyRoXx: Und wie soll mir das jetzt helfen? Wenn du den zitierten Satz zu Ende liest, dann siehst du, dass es mit erase() auch nicht geklappt hat. Habe ich es vielleicht falsch gemacht?

    for (int i = 5000000 - 1; i >= 0; -- i)
    	test.erase(test.begin() + i);
    


  • snOOfy schrieb:

    Ich habe einen Vektor mit Zeigern auf Elemente einer Klasse. Die Elemente des Vektors müssen also mit new() erstellt werden.

    Falsch. Müssen sie nicht. Können aber.

    snOOfy schrieb:

    vector<CTest*> test;
    	for (int i = 0; i < 5000000; ++ i)
    		test.push_back(new CTest());
    	system("PAUSE");
    

    Wenn ich später aber Elemente des Vektors mit erase() lösche, wird der Speicher nicht wieder freigegeben. Muss ich die Elemente zusätzlich noch mit delete löschen?

    Dem Vektor ist das schei*egal, was er da speichert. Für Zeiger gibt es keine Sonderbehandlung. Wenn ein (normaler, roher) Zeiger zerstört wird, wird nicht automatisch auch der Speicher freigegeben, auf den er zeigte.

    snOOfy schrieb:

    Das habe ich nämlich versucht:

    for (int i = 0; i < 5000000; ++ i)
    	delete test[i];
    

    Der Speicher wird dadurch aber auch nicht freigegeben.

    Wie willst Du das denn festgestellt haben?



  • Wie willst Du das denn festgestellt haben?

    Im Windows-Taskmanager sehe ich, dass das Programm nach der Löschaktion immernoch 730MB Arbeitsspeicher belegt, genau wie vorher.


  • Mod

    snOOfy schrieb:

    Wie willst Du das denn festgestellt haben?

    Im Windows-Taskmanager sehe ich, dass das Programm nach der Löschaktion immernoch 730MB Arbeitsspeicher belegt, genau wie vorher.

    Und was hat das miteinander zu tun?

    (Tipp: Nichts)



  • volkard schrieb:

    test.clear();
    

    http://www.cplusplus.com/reference/stl/vector/clear/

    es ist nur garantiert, dass nach dem aufruf von clear() die größe des vektors (size()) null ist, nicht aber dessen capacity.

    die standardlösung dafür ist

    vector<T>().swap(other_vector);
    

  • Mod

    davie schrieb:

    die standardlösung dafür ist

    vector<T>().swap(other_vector);
    

    Man sollte aber betonen, dass dies nicht die Standardlösung zum Löschen der Vectorelemente ist, sondern die Standardlösung für den eher speziellen und seltenen Fall in dem man die capacity des Vectors ebenfalls auf 0 setzen möchte.



  • SeppJ schrieb:

    Und was hat das miteinander zu tun?
    (Tipp: Nichts)

    Im Laufe der Zeit werden in meinem Vektor immer mehr Elemente erstellt und alte gelöscht. Die Anzahl der Elemente bleibt ungefähr gleich. Trotzdem verbraucht das Programm aber immer mehr Arbeitsspeicher, bis es bei 2GB schließlich abstürzt. Wie hat das jetzt nichts damit zu tun, dass der Speicher meiner Vektorelemente nicht freigegeben wird? Genau das führt doch zu dem Problem, oder nicht?

    Vielleicht stelle ich mich ja ein bisschen blöd an, aber was genau muss ich denn jetzt machen, damit immer gleich viel Arbeitsspeicher belegt bleibt (nämlich genau der, der für die vorhandenen Elemente des Vektors benötigt wird)?



  • snOOfy schrieb:

    Im Laufe der Zeit werden in meinem Vektor immer mehr Elemente erstellt und alte gelöscht. Die Anzahl der Elemente bleibt ungefähr gleich. Trotzdem verbraucht das Programm aber immer mehr Arbeitsspeicher, bis es bei 2GB schließlich abstürzt.

    Dann hast Du an anderer Stelle ein Speicherleck. Solange die Kapazität nicht wachsen muss, werden die Elemente eines Vektors immer recycled. Vielleicht machst Du bei der Freigabe des Freispeichers mittels delete was falsch.



  • Das hier ist der gesamte Code:

    #include <vector>;
    using namespace std;
    
    class CTest {
    	double asd;
    };
    
    int main() {
    	vector<CTest*> test;
    	for (int i = 0; i < 2000000; ++ i)
    		test.push_back(new CTest());
    	system("PAUSE");
    
    	for (int i = 2000000 - 1; i >= 0; -- i)
    		delete test[i];
    	system("PAUSE");
    }
    

    Mache ich da bei delete was falsch?



  • snOOfy schrieb:

    Das hier ist der gesamte Code:

    #include <vector>;
    using namespace std;
    
    class CTest {
    	double asd;
    };
    
    int main() {
    	vector<CTest*> test;
    	for (int i = 0; i < 2000000; ++ i)
    		test.push_back(new CTest());
    	system("PAUSE");
    
    	for (int i = 2000000 - 1; i >= 0; -- i)
    		delete test[i];
    	system("PAUSE");
    }
    

    Mache ich da bei delete was falsch?

    Der Code verbaucht auf typischen 32 Bit Systemen knapp 23 MB. Wie Du damit 2 GB voll bekommst, erschließt sich mir nicht.



  • Jedenfalls ist klar, warum du beim zweiten system("PAUSE") immer noch einiges an Speicher benötigst (laut TaskMgr). Da du zwar die Objekte gelöscht hast, aber immer noch alle Zeiger im Speicher hast (auch wenn diese Zeiger jetzt auf ungültigen Speicher zeigen). Nach der for-Schleife musst du noch vec.clear() bzw. zum endgültigen Bereinigen (= vec.capacity() ==0) musst du die Zeile mit dem swap einfügen.

    MfG SideWinder



  • snOOfy schrieb:

    Mache ich da bei delete was falsch?

    Sollte so klappen, sofern 'int' solch große Zahlen fassen kann (was nicht garantiert ist, btw). Es ist aber eher sinnfrei in diesem Fall konkreten Beispiel new/delete zu verwenden. Ich frage micht, was Dir diese Indirektion bringt. Du kannst doch auch gleich die CTest-Objekte im Vektor speichern, statt Zeiger...

    Viele kleine "popelige" Objekte per new Erzeugen, würde ich vermeiden, wenn's geht. So verbrätst Du wahrscheinlich 20 Bytes pro Objekt (nur geraten, 8 Bytes für's double, 8 bytes für Freispeicher-Verwaltungsoverhead und 4 bytes für ein Zeiger, 32bit-System angenommen) und das Speicher anfordern/freigeben kostet auch Zeit. Da ist das Kopieren im Vergleich dazu bei einem "popeligen" double nicht schlimm.



  • Hallo Tachyon,

    dass Du alles löschtst ist ja nur in Deinem Beispiel so. Im effektiven Code willst Du ja einzelne Elemente löschen.

    for (int i = 2000000 - 1; i >= 0; -- i)
       {
         CTest* heapItem = test[i];
         test.erase (test.begin () + i);
         delete heapItem;
       }
    

    Ich habe das mit dem 'heapItem' absichtlich so gemacht damit ersichtlich wird, dass zwei Dinge gelöscht werden müssen.

    1. Das Item im Vector
    2. Das Item auf dem Heap

    Herzliche Grüsse
    Walter



  • @Tachyon: 2GB verbraucht es nicht hier im Beispiel sondern in meinem richtigen Programm, in dem die Elemente des Vektors Instanzen einer deutlich komplexeren Klasse sind.

    @weicher: Danke, so funktioniert es 🙂

    @krümelkacker: Danke für den Hinweis, ich habe nicht bedacht, was das für einen Overhead produziert. 😮 Jetzt habe ich es auch mal ohne Zeiger umgesetzt und verglichen, der Unterschied ist wirklich gigantisch.

    #include <vector>;
    using namespace std;
    
    class CTest {
    	double asd;
    public:
    	CTest(double asd) { this->asd = asd; };
    };
    
    int main() {
    	// Möglichkeit 1 ohne Zeiger (10sec, 20mb Speicher)
    	vector<CTest> test1;
    	for (int i = 0; i < 2000000; ++ i) {
    		test1.push_back(CTest(i));
    	}
    	for (int i = 2000000 - 1; i >= 0; -- i) {
    		test1.erase(test1.begin () + i);
    	}
    
    	// Möglichkeit 2 mit Zeigern (5min, 170mb Speicher)
    	vector<CTest*> test2;
    	for (int i = 0; i < 2000000; ++ i)
    		test2.push_back(new CTest(i));
    	for (int i = 2000000 - 1; i >= 0; -- i) {
    		CTest* heapItem = test2[i];
    		test2.erase(test2.begin () + i);
    		delete heapItem; 
    	}
    	system("PAUSE");
    }
    


  • Wenn du große Objekte speicherst und der Vektor oft umkopiert wird (z.B. durch wahlfreies Löschen), ist die Heap-Variante aber wieder deutlich schneller.



  • wx++ schrieb:

    Wenn du große Objekte speicherst und der Vektor oft umkopiert wird (z.B. durch wahlfreies Löschen), ist die Heap-Variante aber wieder deutlich schneller.

    In so einem Fall könnte man sich Gedanken über eine std::list machen.



  • Diese Schleife, in der Du test1.erase(...) aufrufst, ist sinnfrei. Ein einfaches test1.clear(); tut es auch.

    Wenn deine Objekte aber im wirklichen Programm "komplex" sind (im Sinne eines zeitaufwändigen Kopierkonstruktors), kannst Du ja auch mal std::deque statt std::vector probieren. Wenn ich mich da nicht vertue, dürfte man sich mit einer deque beim Wachsen einiges an Kopierarbeit sparen. Die Komplexitätsklasse beim push_back ist zwar in beiden Fällen die gleiche, aber ich kann mir vorstellen, dass der Faktor ein anderer ist. Der Extremfall ist dann std::list. Aber damit verlierst Du dann den "random access".

    lg,
    k



  • wx++ schrieb:

    Wenn du große Objekte speicherst und der Vektor oft umkopiert wird (z.B. durch wahlfreies Löschen), ist die Heap-Variante aber wieder deutlich schneller.

    In der Regel nicht mal das, dazu müssten die Objekte sehr groß sein. Grade im konrekten Fall ist der Unterschied zwischen einem 4-Byte-Pointer und einem 8-Byte-double minimal im Vergleich zum Allokationsoverhead.
    Vielmehr ist die Faustregel, wann immer möglich zuerst die Variante ohne Zeiger probieren (einfacher, sicherer, in der Regel performanter) und erst wenn sich das als Performancebottenleck herausstellen sollte probieren, ob es mit Zeigern schneller geht.



  • wx++ schrieb:

    Wenn du große Objekte speicherst und der Vektor oft umkopiert wird (z.B. durch wahlfreies Löschen), ist die Heap-Variante aber wieder deutlich schneller.

    Ja, aber bevor man dann sämtliche Nachteile manueller Speicherverwaltung in Kauf nimmt, kann man auch über Alternativen nachdenken. Abgesehen von anderen Containertypen kann man z.B. mit swap() einige Kopien vermeiden. Insbesondere ist damit auch eine Löschung aus std::vector in O(1) möglich, sofern die Reihenfolge unwichtig ist.



  • Hallo Krümelkacker

    Diese Schleife, in der Du test1.erase(...) aufrufst, ist sinnfrei

    Ja, aber nur in seinem Beispiel und nicht im richtigen Leben 😉

    Herzliche Grüsse
    Walter


Anmelden zum Antworten