Speicherverbrauch shared_ptr und normaler *



  • Hallo zusammen

    Mit einer factory Klasse erzeuge ich diverse Objekte (sehr viele). Bisher habe ich dise via pointer in einer std::map gespeichert.

    Der Arbeitsspeicher ist auf 1,2 GB angestiegen.
    Nun habe ich auf std::shared_ptr umgestellt. Der Speicher wächst nun knapp an die 2GB Grenze und dann bekomme ich eine exception: bad allocation.

    Kann man da irgendwie "sparsamer" arbeiten. Oder muss ich wieder auf die normalen pointer wechseln?


  • Mod

    Wer ist der Besitzer der Objekte?



  • Warum std::shared_ptr und nicht std::unique_ptr?



  • 1. Brauchst du wirklich alle Objekte dauerhaft?
    2. Ist eine std::map wirklich der richtige Container für deinen Usecase?



  • Wieviele Objekte sind das denn? Tausende? Millionen?

    Und versuch doch mal, sizeof Objekt, sizeof shared_ptr und sizeof map::value_type auszugeben, um rauszufinden, wer genau den Speicher verbraucht.



  • Der shared_ptr selbst braucht typischerweise doppelt so viel wie ein roher Zeiger.

    Und pro Pointee der über shared_ptr verwaltet wird kommt nochmal was dazu. Wie viel genau hängt von etlichen Faktoren ab, ich würde aber mal mit der Grössenordnung 4x Roher-Zeiger rechnen.


  • Mod

    hustbaer schrieb:

    Der shared_ptr selbst braucht typischerweise doppelt so viel wie ein roher Zeiger.

    Und pro Pointee der über shared_ptr verwaltet wird kommt nochmal was dazu. Wie viel genau hängt von etlichen Faktoren ab, ich würde aber mal mit der Grössenordnung 4x Roher-Zeiger rechnen.

    Wobei das natürlich gute Gründe hat, nämlich damit der shared_ptr das machen kann, was er macht. Im Prinzip gibt es hier drei Möglichkeiten:
    1. shared_ptr ist unpassend.
    2. shared_ptr ist passend und man kann nichts besseres tun, weil alles so notwendig ist.
    3. shared_ptr ist zwar passend, aber man kann eine kreative Lösung finden, dass man mit Trickserei doch noch Speicher spart, weil man irgendeine weitere Eigenschaft der Anforderungen ausnutzt.

    Das der TE noch nicht auf die Gegenfragen geantwortet hat, kann man nichts weiter dazu sagen.



  • Ja klar ist der Speicherverbrauch OK. Und klar hast du Recht dass es gut wäre zu wissen was der OP überhaupt macht und wieso er überhaupt shared_ptr verwenden will.

    Dachte mir nur ich könnte die Frage zur Abwechslung auch einfach mal beantworten 🙂


  • Mod

    Das richtete sich weniger an dich als an den Threadersteller, damit er versteht, warum man seine Frage nicht so gut beantworten kann, solange er nicht mit mehr Details raus rückt.



  • Hallo

    Versuche mal die Fragen zu beantworten.
    1. Wer ist der Besitzer der Objekte?
    Objekte werden von einer Factory gespeichert. Und in der map abgelegt. Map ist Besitzer.

    2. Warum std::shared_ptr und nicht std::unique_ptr?
    Benötigt unique_ptr weniger speicher als shared_ptr?

    3.Brauchst du wirklich alle Objekte dauerhaft?
    Eigentlich schon, aber zumindest einmal beim Laden. Aber ich bekomme ja nicht alle geladen. Stürzt ja vorher ab.

    4. Ist eine std::map wirklich der richtige Container für deinen Usecase?
    Ich benötige die map mit einem string als Key um nach bestimmten Objekten zu suchen. Denke schon, das hier map das richtige ist.

    5. Wieviele Objekte sind das denn? Tausende? Millionen?
    3 bis 4 hunderttausend -> schwankend

    6. Wieso er überhaupt shared_ptr verwenden will?
    `Na für den Komfort. Man kann ja eigentlich das ganze immer mit einem Rohzeiger machen. Man muss sich dann halt selber um das allokieren und aufräumen kümmern.

    Oder wann sollte man auf den shared_ptr verzichten?`


  • Mod

    booster schrieb:

    Versuche mal die Fragen zu beantworten.
    1. Wer ist der Besitzer der Objekte?
    Objekte werden von einer Factory gespeichert. Und in der map abgelegt. Map ist Besitzer.

    Eindeutiger Besitzer -> unique_ptr.

    booster schrieb:

    2. Warum std::shared_ptr und nicht std::unique_ptr?
    Benötigt unique_ptr weniger speicher als shared_ptr?

    Auch. Aber in erster Linie macht er das, was du willst (map ist Besitzer der Objekte), shared_ptr hingegen nicht.



  • unique_ptr geht nicht.

    Doch etwas komplizierter. Es ist keine Flache map. Sondern die gespeicherten Objekte haben wiederum eine map mit Objekten gleichen typs. Hier wiederum benötige ich den Zeiger auf das Elternelement.

    Und eine Kopie von unique_ptr geht nicht.

    Es wird hier die Variablenstruktur einer SPS dargestellt.



  • booster schrieb:

    5. Wieviele Objekte sind das denn? Tausende? Millionen?
    3 bis 4 hunderttausend -> schwankend

    Wenn du 400.000 Objekte erzeugst, die zusammen 2 GB Speicher fressen, dann ist jedes Objekt ungefähr 10.700 Bytes groß. Auf die paar Bytes für den Smartpointer kommt es dabei nicht an.

    10 KB um eine SPS-Variable darzustellen kommt mir enorm viel vor. Schleppen die Objekte die gesamte Benutzerdoku mit sich herum oder wie kommt das?



  • booster schrieb:

    Doch etwas komplizierter. Es ist keine Flache map. Sondern die gespeicherten Objekte haben wiederum eine map mit Objekten gleichen typs. Hier wiederum benötige ich den Zeiger auf das Elternelement.

    shared_ptr im Kreis? Blöde Idee. Wann wird der Speicher wohl freigegeben?

    Benutze std::uniqe_ptr in der Map und Referenzen oder Pointer für "das Elternelement".



  • booster schrieb:

    unique_ptr geht nicht.

    Doch etwas komplizierter. Es ist keine Flache map. Sondern die gespeicherten Objekte haben wiederum eine map mit Objekten gleichen typs. Hier wiederum benötige ich den Zeiger auf das Elternelement.

    Und eine Kopie von unique_ptr geht nicht.

    Es wird hier die Variablenstruktur einer SPS dargestellt.

    Als Zeiger auf das Elternelement kannst du ruhig einen Raw Pointer verwenden. In der Regel will man ja keinen Besitz am Parent ausdrücken oder haben.
    Wo du den Unique Ptr kopieren willst, sehe ich auch nocht nicht.

    Wenn die map die Objekte besitzt, könnte man auch überlegen, ob man auf die Pointer da nicht ganz verzichtet und die Objekte halt direkt von der Map verwalten lässt.

    Aber all das hilft dir nicht, den benötigten Speicher dramatisch zu reduzieren. Ebenso kann ich mir nicht vorstellen, dass du durch die Umstellung von Raw pointer auf Shared Ptr einen Mehrbedarf an Speicher von 80% hast. Möglicherweise hältst du jetzt Objekte am Leben, weil noch ein Shared Pointer darauf zeigt, die du früher schon zerstört hast (und möglicherweise Pointer vorgehalten hast, die ins Nirwana zeigen).

    Und, bad_alloc bei 2GB? Auf was für einem System arbeitest du?



  • @Printe:
    Sorry. Falsch. Durch deinen Hinweis habe ich nochmals kontrolliert sind über ne Million.

    @manni66:
    Der Speicher wird freigegeben wenn die map geleert wird!?

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Und in den Klassen die von der map den zeiger anfordern? Dann muss ich ja von dem unique ptr eine Kopie zurück geben?



  • Nein, Manni meint, gar kein Shared Ptr. Shared Ptr heißt, geteilter Besitz, also das dass Objekt so lange existiert, wie einer der Pointer darauf zeigt. Und, dass willst du bei einer Kind -> Eltern Beziehung üblicherweise nicht.



  • booster schrieb:

    @Printe:
    Sorry. Falsch. Durch deinen Hinweis habe ich nochmals kontrolliert sind über ne Million.

    Und wenn schon - das sind immer noch 4 kB pro Objekt.

    1. Frage: Kann das sein? Sind deine Objekte derart groß? Falls nicht, liegt das Problem woanders. Evtl. hältst du irgendwo große Speichermengen fest und ahnst nichts davon - das ist das, was Schlangenmensch meinte.

    2. Frage: Falls das sein kann: Muss das auch so sein? Gibt es an SPS-Variablen so viel zu speichern? Oder kannst du Teile davon auf der Festplatte lassen und nur bei Bedarf nachladen? Irgendwelche beschreibenden Infos zum Beispiel?

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Nein. Um Kreis-Referenzen mit shared_ptr zu vermeiden, gibt es als drittes Verwaltungsobjekt den weak_ptr. Nachlesen und schlau werden.

    Und in den Klassen die von der map den zeiger anfordern? Dann muss ich ja von dem unique ptr eine Kopie zurück geben?

    Du kannst auch mit unique_ptr::get einen Raw-Pointer aufs verwaltete Objekt holen. Der Anforderer darf nur nicht auf die Idee kommen, dieses Objekt freizugeben, er muss es nach Gebrauch einfach loslassen.

    Eigentlich ist das aber dreckig und unterläuft das mühsam eingeführte Objektmanagement gleich wieder.



  • booster schrieb:

    @manni66:
    Der Speicher wird freigegeben wenn die map geleert wird!?

    Vermutlich nein.

    booster schrieb:

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Nein!
    Und wo kommt denn jetzt auf einmal next her?

    SeppJ schrieb:

    Das richtete sich weniger an dich als an den Threadersteller, damit er versteht, warum man seine Frage nicht so gut beantworten kann, solange er nicht mit mehr Details raus rückt.

    Dein Salamitaktik wird dir nicht helfen.



  • @Schlangenmensch
    Ok da gebe ich dir recht das ich intern mit Rohzeigern arbeiten kann.

    Ist allerdings etwas aufwendiger, als wenn ich immer mit shared_ptr arbeite.

    Aber gut. Die Frage ist aber dann immer noch die Rückgabe meiner find Methode()
    Die Aktuell so aussieht

    shared_ptr<TcAdsSymbolInfo> FindSymbol(const string& name) const
    {
        const auto pos = _infoMap.find(name);
    
        if (pos != _infoMap.end())
        {
            return pos->second;
        }
    
        for (const auto& info : _infoMap)
        {
            const auto symbol = info.second->SubSymbols->FindSymbol(name);
            if (symbol != nullptr)
            {
                return symbol;
            }
        }
    
        return nullptr;
    }
    

    wenn meine map nun einen unique ptr beinhaltet kann ich diesen ja so nicht zürck geben


Anmelden zum Antworten