In C++ gibt es die Wahl zwischen verschiedenen Speicherstrategien. Neben der häufig verwendeten dynamischen Allokation und Allokation auf dem Stack, lässt sich der Speicher auch statisch zum Startzeitpunkt des Programms anfordern. Dies kann ein fester Block sein oder auch ein oder mehrere Memory Pools. Jeder dieser Strategien besitzt natürlich ihre Vor- und Nachteile. Genau diese Frage will dieser Artikel klären.
Im letzten Artikel habe ich vier typische Strategien vorgestellt, Speicher anzufordern. Mit diesem Artikel gehe ich einen Schritt weiter, indem ich die Vor- und Nachteile der verschiedenen Strategien in einer Tabelle gegenüberstelle. Zuerst werde ich aber die Kriterien klären, die Grundlage meines Vergleichs ist.
Die Vergleichspunkte
Fragmentierung
Interne und externe Fragmentierung sind zwei große Probleme der dynamischen Speicherallokation und daher auch der Memory Pools.
- Interne Fragmentierung
- Ein Objekt benötigt nicht den ganzen Speicher, der für es angelegt wurde.
- Externe Fragmentierung
- Die Speicherbereiche zwischen den verwalteten Objekten ist zu klein, um für weitere Objekte verwendet werden zu können.
- Interessanterweise neigt ein System, dass für interne Fragmentierung optimiert ist verstärkt zur externen Fragmentierung. Diese Beziehung gilt auch anders herum. So ist die interne Fragmentierung je geringer, desto exakter die Speicherbereiche auf die Objekte zugeschnitten sind. Damit sind die Objektgrößen in der Regel aber sehr unregelmäßig und der verfügbare Platz kann nicht optimal genutzt werden. Werden hingegen gleich große Speicherbereiche angelegt, so steigt die interne Fragmentierung, da die Objekte in der Regel in zu großen Speicherbereichen angelegt werden.
- Eine Lösung dieses Widerspruchs besteht darin, die Speicherbereiche an die Objektgrößen anzupassen und die Speicherbereiche immer wieder zu verschieben und optimal auszurichten. Damit wird interne Fragmentierung per se und die externe Fragmentierung optimiert. Dies bedeutet natürlich zusätzlichen Aufwand zur Laufzeit und steht damit im Widerspruch zur Vorhersagbarkeit der Speicheranforderungen.
Speicher Mangel
Die Vorteile der dynamischen Allokation besteht vor allem darin, dass der vorhandene Speicher schier unerschöpflich erscheint. Das gilt aber nicht für einen festen Block, der zur Startzeit angefordert wird oder auch für die Stack Allokation.
Speicherfreigabe
Sehe ich von Smart Pointer wie std::unitque_ptr oder auch std::shared_ptr ab, so muss der Speicher explizit bei der dynamischen Allokation und auch der Pool Allokation freigegeben werden. Dies gilt nicht für die Stack Allokation oder auch die Allokation eines festen Blocks. Bei der Stack Allokation räumt die C++ Laufzeit automatisch auf, bei der Allokation eines festen Blockes wird in der Regel keine Speicherfreigabe angewandt.
Wird der Speicher nicht freigegeben, entstehen Speicherlecks.
Speicherlecks
Die Stack Allokation ist per se frei von Speicherlecks. Dies gilt aber nicht für die drei weiteren Speicherallokationen. Insbesondere bei der dynamischen Allokation und dem Memory Pool sind Speicherlecks eine große Herausforderung.
Vorhersagbarkeit
Vorhersagbarkeit spielt insbesondere in Systemen eine große Rolle, in der harte Echtzeitkriterien gelten. Das heißt, es gibt ein Zeitfenster, in der eine Speicherallokation ausgeführt werden muss. Diese Garantie kann dynamische Allokation natürlich nicht geben, da der Speicher zum Beispiel stark fragmentiert ist. Daher kann die dynamische Speicherallokation oft in embedded Echtzeitsystemen nicht eingesetzt werden. Dies gilt aber nicht für die drei weiteren Arten, Speicher zu allokieren. Hier ist das Zeitverhalten deterministisch.
Benutzerfreundlichkeit
Aus Anwendersicht bieten alle vier Speicherallokationen ihre Vor- und Nachteile. So ist bei der dynamischen Speicherallokation und dem Memory Pool der Anwender in der Regel in der Pflicht, seinen Speicher sorgfältig zu verwalten. So muss er explizit delete oder free aufrufen um Speicherlecks zu vermeiden. Zwar ist er bei der Allokation eines Blocks und der Stack Allokation von dieser Pflicht entbunden, hat aber dafür einige Einschränkungen.
So erlaubt ihm die Allokation eines Blocks keine beliebige Anzahl an Speicherallokationen, so ist die Größe des Stacks beschränkt. Auf dem Stack lassen sich in C++ keine dynamischen Objekte anlegen und die Lebenszeit eines Objekts ist an die Lebenszeit seines Stacks gebunden.
Variabilität
Die Variabilität ist sicher das große Argument für die dynamische Speicherallokation. Dies trifft natürlich auch bedingt für den Memory Pool zu, wenn Memory Pools verschiedener Größe angelegt wurden oder die Memory Pools ausreichend groß sind. Damit kann das Programm auch außergewöhnliche Speicheranforderungen beantworten. Das gilt nicht für Stack Allokation und die Allokation eines Blocks. Beide Speichertechniken setzen vorhersagbbare Speicheranforderungen voraus.
Das große Bild
Zum Abschluss gibt es alle Punkte nochmals in einer großen Tabelle.
Ein paar Worte noch zu der Tabelle. Beim Memory Pool habe ich öfters Ja/Nein als Antwort gewählt. Dies hängt natürlich davon ab, ob ein Memory Pool für alle Objekte, oder mehrere Memory Pools zur Verfügung stehen. Auf einen Punkt will ich explizit hinweisen. Ein Memory Pool bietet das Beste aus beiden Welten an. Zum einen unterstützt er ähnlich wie die dynamische Allokation variable Speicheranforderungen, indem die Speicheranforderungen zur Laufzeit auf statisch reservierte Speicherbereiche abgebildet werden. Zum anderen ist der Zeitbedarf für Speicheranforderungen wie bei der Stack Allokation oder der Allokation eines Blocks deterministisch. Diese besonderen Stärken des Memory Pool spiegelt die Tabelle wieder.
Wie geht's weiter?
Jonathan Müller, Autor des englischsprachigen Blogs foonathan::blog() und vor allem Autor der memory Bibliothek ("STL compatible C++ memory allocator library using a new RawAllocator concept that is similar to an Allocator but easier to use and write."), wird im nächsten Artikel einen Beitrag zu Memory Pool Allokatoren auf diesem Blog schreiben. Das freut mich sehr, denn wenn ich ein Punkt in meinen letzten Artikel über das Speichermanagement mit C++ gelehrt hat, dann ist er der, dass dieses Thema sehr viel Expertise voraussetzt.
Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library". Hole dir dein E-Book. Unterstütze meinen Blog.
Weiterlesen...