Close
c++_memory_arena_implications_blog

C++ Speicherregionen und ihre Auswirkungen

robert seilbeck
von Robert Seilbeck
Dezember 11, 2019
Lesezeit: 5 Minuten

Speicheregionen wurden in der voreingestellten C++ Speicherzuordnung unter Linux eingefĂŒhrt, um die LeistungsfĂ€higkeit speicherintensiver Multithread-Anwendungen zu verbessern. Zuvor musste jede Speicherzuordnung synchronisiert werden. Dies fĂŒhrte dazu, dass die Speicherzuordnung hĂ€ufig LeistungsengpĂ€sse verursachte.

Speicherregionen lösen dieses Problem durch die Einrichtung mehrerer Speicher-Pools. Diese unterstĂŒtzen die Speicherzuordnung mit mehreren simultanen Threads (mehr ĂŒber Speicherregionen erfahren Sie in diesem hervorragenden Blogbeitrag: https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/).

In dem Blogbeitrag geht es um die Frage, wie sich dies auf das beobachtete Speicherverhalten auswirkt. Diese Frage ist deshalb relevant, weil es fÀlschlicherweise leicht als Speicherleak interpretiert werden kann.

So werden Speicherregionen fÀlschlicherweise als Speicherleaks interpretiert

Das Programm startet mehrere separate Workerthreads. Dabei gibt es zwei verschiedene Arten von Threads: 30MB-Workerthreads und 100MB-Workerthreads. Der 30MB-Workerthread weist 30 MB Daten zu und initialisiert sie.

Die Daten werden nach 100 Millisekunden freigegeben. Das passiert zehnmal nacheinander. Der 100MB-Workerthread tut genau dasselbe, allerdings mit 100MB-Blöcken. Welche Art von Workerthread und wie viele davon ausgelöst werden, lĂ€sst sich ĂŒber Befehlszeilenparameter einstellen.

Sobald alle Workerthreads ihre Arbeit getan haben, wartet das Programm auf weitere Benutzerbefehle. Der Benutzer hat drei Möglichkeiten: Die erste beendet das Programm, die zweite fĂŒhrt malloc_trim(0) aus und die dritte druckt ĂŒber malloc_stats() Statistiken zur Speicherregion aus.

Die folgenden Versuche wurden unter Ubuntu 16.04 mit einer 8-Kern CPU (4 physische) durchgefĂŒhrt.

Die Tabelle unten veranschaulicht den residenten Prozessspeicher fĂŒr unterschiedliche Worker-Typen und -Anzahlen, nachdem alle Worker ihre Arbeit erledigt und sĂ€mtliche Daten freigegeben haben.

ubuntu worker types and worker number combinations

Die 900 MB residenter Speicher nach dem vierten Durchlauf lohnen einen genaueren Blick. Die naheliegende Vermutung wĂ€re ein Speicherleck, das sich aber mithilfe von Tools wie Valgrind ausschließen lĂ€sst. Die eigentliche Ursache finden wir, wenn wir einen Blick auf die Statistiken zu den Speicherregionen werfen, die wir ĂŒber malloc_stats() erhalten. Beim ersten Durchlauf erzeugt malloc_stats() beispielsweise die folgende Ausgabe:

c plus arena statistics using malloc stats

Eine ErlĂ€uterung dieses Konzepts finden Sie in diesem Artikel, der auf einem „Lightning Talk“ basiert, welcher anlĂ€sslich einer Konferenz der C++ Anwendergruppe in MĂŒnchen gehalten wurde und beschreibt, wie Speicherregionen zu vermeintlichen Speicherlecks fĂŒhren können. In beiden FĂ€llen wird das folgende Programm verwendet: https://github.com/celonis-se/memory-arena-example/blob/master/main.cpp

Testergebnisse nach Speicherregion

Die Ausgabedaten zeigen, wie viele Speicherregionen vorhanden sind, wie groß diese sind und wie viel Speicher tatsĂ€chlich genutzt wird. Die Ergebnisse der einzelnen TestlĂ€ufe finden sich in der folgenden Tabelle:

c plus arena test results

Die Tests deuten darauf hin, dass fĂŒr jeden Workerthread eine eigenstĂ€ndige Speicherregion erstellt wird. Außerdem scheint es so, als machten die Speicherregionen keinen Speicherplatz fĂŒr das Betriebssystem verfĂŒgbar, obwohl dieses vollkommen leer ist.

Die Unterschiede in der GrĂ¶ĂŸe der Speicherregionen zwischen den 100MB-Workerthreads und den 30MB-Workerthreads lĂ€sst sich dadurch erklĂ€ren, dass große zugewiesene Speicherblöcke anders als kleine behandelt werden. Denn wĂ€hrend kleinere Speicherblöcke einer bestimmten Speicherregion zugeordnet werden, erfolgt die Zuordnung grĂ¶ĂŸerer Blöcke per mmap, um eine ĂŒbermĂ€ĂŸige Fragmentierung zu vermeiden. Weitere Informationen zu diesem Thema finden Sie hier: https://www.linuxjournal.com/article/6390

Im Hinblick auf das Problem der fehlenden RĂŒckgabe des Speicherplatzes an das Betriebssystem empfiehlt sich die LektĂŒre eines Posts auf stackoverflow zum Thema Speichermanagement in Multi-Thread-Umgebungen mit malloc_trim.

Verwendung von Malloc_Trim

Die folgende Tabelle zeigt die Statistiken zu den Speicherregionen nach der DurchfĂŒhrung von malloc_trim. Laut Dokumentation versucht dieses, von oben freien Speicherplatz aus dem Heap freizugeben:

arena states applying malloc trim

Die Tests zeigen, dass nur 30 MB – die GrĂ¶ĂŸe einer Speicherregion – freigegeben werden. Weitere Untersuchungen zeigen, dass malloc_trim ausschließlich die ungenutzten Bytes aus dem Hauptspeicher an das Betriebssystem zurĂŒckgibt.

Nach unserem Kenntnisstand existiert derzeit keine Möglichkeit, den Speicherplatz fĂŒr das Betriebssystem nutzbar zu machen, was wiederum einen betrĂ€chtlichen Speicher-Overhead verursachen kann.

Um diesen zu begrenzen, ist die Anzahl der Speicherregionen standardmĂ€ĂŸig auf das Achtfache der Anzahl an Cores begrenzt (einschl. Hyperthreads). Da das hier verwendete GerĂ€t acht Kerne besitzt, sind somit 64 Speicherregionen möglich.

8 core machine 64 arenas results

Wenn mehr als 64 Workerthreads aktiv sind, werden keine weiteren Speicherregionen erstellt. Stattdessen können die vorhandenen aber wachsen.

Speicherregionen in C++ – Fazit

Speicherregionen können die LeistungsfĂ€higkeit deutlich verbessern, aber auch einen betrĂ€chtlichen Speicher-Overhead verursachen. Das gilt insbesondere fĂŒr Multi-Threaded-Programme mit langer Laufzeit.

Dieses Verhalten kann leicht als Speicherleck missverstanden werden. Bestehen Zweifel, ob tatsĂ€chlich ein Speicherleck vorliegt oder der Speicher in Speicherregionen aufgeteilt worden ist, kann dies ĂŒber malloc_stats() mithilfe der Statistiken ĂŒberprĂŒft werden.

robert seilbeck
Robert Seilbeck
Head of Back-end Development, Celonis

Abonnieren Sie unseren monatlichen Newsletter

We've received your submission
Please fill in all the fields

Indem Sie dieses Formular absenden, stimmen Sie der Speicherung und Verarbeitung Ihrer personenbezogenen Daten durch Celonis gemĂ€ĂŸ unserer Datenschutzrichtlinie zu.
Lieber Besucher, wir haben festgestellt, dass Sie eine veraltete Browser-Version verwenden. Teile dieser Seite werden von Ihrem Browser nicht korrekt dargestellt. FĂŒr eine korrekte Funktionsweise der Seite empfehlen wir Ihnen, einen alternativen Browser zu verwenden oder Ihren Browser auf eine unterstĂŒtzte Version anzuheben.