Dies ist Version 8.3.0 (nächste Release-Entwicklung) eines konservativen Garbage Collectors für C und C++.
Lizenz: MIT-Stil
Möglicherweise finden Sie eine neuere/stabilere Version auf der Download-Seite oder auf der BDWGC-Website.
Außerdem sind die neuesten Fehlerbehebungen und neuen Funktionen im Entwicklungs-Repository verfügbar.
Dabei handelt es sich um einen Allzweck-Speicherzuteiler für die Müllsammlung. Die verwendeten Algorithmen sind beschrieben in:
Boehm, H. und M. Weiser, „Garbage Collection in an Uncooperative Environment“, Software Practice & Experience, September 1988, S. 807–820.
Boehm, H., A. Demers und S. Shenker, „Mostly Parallel Garbage Collection“, Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design and Implementation, SIGPLAN Notices 26, 6 (Juni 1991), S. 157- 164.
Boehm, H., „Space Efficient Conservative Garbage Collection“, Proceedings of the ACM SIGPLAN '91 Conference on Programming Language Design and Implementation, SIGPLAN Notices 28, 6 (Juni 1993), S. 197-206.
Boehm H., „Reducing Garbage Collector Cache Misses“, Tagungsband des International Symposium on Memory Management 2000.
Mögliche Interaktionen zwischen dem Kollektor und optimierenden Compilern werden in besprochen
Boehm, H. und D. Chase, „A Proposal for GC-safe C Compilation“, The Journal of C Language Translation 4, 2 (Dezember 1992).
Boehm H., „Simple GC-safe Compilation“, Tagungsband der ACM SIGPLAN '96 Conference on Programming Language Design and Implementation.
Anders als der in der zweiten Referenz beschriebene Kollektor arbeitet dieser Kollektor entweder mit angehaltenem Mutator während der gesamten Sammlung (Standard) oder inkrementell während der Zuweisungen. (Letzteres wird auf weniger Maschinen unterstützt.) Auf den gängigsten Plattformen kann es mit oder ohne Thread-Unterstützung erstellt werden. Auf einigen Plattformen kann ein Multiprozessor genutzt werden, um die Speicherbereinigung zu beschleunigen.
Viele der dem Sammler zugrunde liegenden Ideen wurden zuvor von anderen erforscht. Bemerkenswert ist, dass einige der Laufzeitsysteme, die in den frühen 1980er Jahren bei Xerox PARC CSL 84-7). Doug McIlroy hat einen einfacheren, vollständig konservativen Collector geschrieben, der Teil der Version 8 von UNIX (tm) war, aber offenbar keine weit verbreitete Verwendung gefunden hat.
Im Lieferumfang sind rudimentäre Werkzeuge zur Verwendung des Kollektors als Lecksucher enthalten, ebenso wie ein ziemlich ausgeklügeltes Schnurpaket „Kabel“, das den Kollektor nutzt. (Siehe README.cords und H.-J. Boehm, R. Atkinson und M. Plass, „Ropes: An Alternative to Strings“, Software Practice and Experience 25, 12 (Dezember 1995), S. 1315-1330. Dies ist dem „rope“-Paket in Xerox Cedar oder dem „rope“-Paket in der SGI STL- oder der g++-Distribution sehr ähnlich.)
Weitere Sammlerdokumentationen finden Sie in der Übersicht.
Einige der bekannten Verwendungszwecke des Collectors sind auf der GitHub-Seite „Bekannte Clients“ aufgeführt.
Dies ist ein Garbage-Collection-Speicherzuteiler, der als Plug-in-Ersatz für Cs Malloc verwendet werden soll.
Da der Collector keine Kennzeichnung von Zeigern erfordert, versucht er nicht sicherzustellen, dass der gesamte nicht zugängliche Speicher zurückgefordert wird. Unserer Erfahrung nach ist es jedoch in der Regel erfolgreicher bei der Rückgewinnung ungenutzten Speichers als die meisten C-Programme, die explizite Freigaben verwenden. Im Gegensatz zu manuell verursachten Lecks bleibt die Menge des nicht freigegebenen Speichers normalerweise begrenzt.
Im Folgenden wird ein „Objekt“ als ein Speicherbereich definiert, der von den unten beschriebenen Routinen zugewiesen wird.
Auf alle Objekte, die nicht gesammelt werden sollen, muss entweder von anderen solchen zugänglichen Objekten oder von den Registern, dem Stapel, den Daten oder statisch zugewiesenen BSS-Segmenten aus verwiesen werden. Zeiger aus dem Stapel oder den Registern können auf eine beliebige Stelle innerhalb eines Objekts verweisen. Das Gleiche gilt für Heap-Zeiger, wenn der Kollektor mit der Definition ALL_INTERIOR_POINTERS
kompiliert wird oder GC_all_interior_pointers
anderweitig festgelegt ist, wie es jetzt die Standardeinstellung ist.
Das Kompilieren ohne ALL_INTERIOR_POINTERS
kann die versehentliche Beibehaltung von Müllobjekten reduzieren, indem Zeiger vom Heap auf den Anfang eines Objekts erforderlich sind. Für die meisten Programme, die nur einen kleinen Teil des möglichen Adressraums belegen, scheint dies jedoch kein wesentliches Problem mehr zu sein.
Es gibt eine Reihe von Routinen, die den Zeigererkennungsalgorithmus modifizieren. GC_register_displacement
ermöglicht die Erkennung bestimmter Innenzeiger, auch wenn ALL_INTERIOR_POINTERS
nicht definiert ist. GC_malloc_ignore_off_page
ermöglicht das Ignorieren einiger Zeiger in die Mitte großer Objekte, wodurch die Wahrscheinlichkeit einer versehentlichen Beibehaltung großer Objekte erheblich verringert wird. Für die meisten Zwecke scheint es am besten zu sein, mit ALL_INTERIOR_POINTERS
zu kompilieren und GC_malloc_ignore_off_page
zu verwenden, wenn Sie Collector-Warnungen durch Zuordnungen sehr großer Objekte erhalten. Weitere Informationen finden Sie hier.
WARNUNG : Zeiger innerhalb des vom Standard-(System-) malloc
zugewiesenen Speichers werden vom Garbage Collector nicht gesehen. Daher kann die Zuordnung von Objekten, auf die nur aus einer solchen Region verwiesen wird, vorzeitig aufgehoben werden. Es wird daher empfohlen, den Standard malloc
nur für Speicherbereiche wie E/A-Puffer zu verwenden, die garantiert keine Zeiger auf Garbage-Collection-Speicher enthalten. Zeiger in automatischen, statischen oder Registervariablen der C-Sprache werden korrekt erkannt. (Beachten Sie, dass GC_malloc_uncollectable
eine ähnliche Semantik wie Standard-malloc hat, aber Objekte zuordnet, die vom Kollektor verfolgt werden.)
WARNUNG : Der Collector weiß nicht immer, wie er Zeiger in Datenbereichen findet, die dynamischen Bibliotheken zugeordnet sind. Dies lässt sich leicht beheben, wenn Sie wissen, wie Sie diese Datenbereiche auf Ihrem Betriebssystem finden (siehe GC_add_roots
). Code dafür unter SunOS, IRIX 5.X und 6.X, HP/UX, Alpha OSF/1, Linux und Win32 ist enthalten und wird standardmäßig verwendet. (Details zu Windows finden Sie in README.win32 und README.win64.) Auf anderen Systemen werden Zeiger aus dynamischen Bibliotheksdatenbereichen möglicherweise nicht vom Kollektor berücksichtigt. Wenn Sie ein Programm schreiben, das darauf angewiesen ist, dass der Kollektor dynamische Bibliotheksdatenbereiche scannt, ist es möglicherweise eine gute Idee, mindestens einen Aufruf von GC_is_visible
einzubinden, um sicherzustellen, dass diese Bereiche für den Kollektor sichtbar sind.
Beachten Sie, dass der Garbage Collector nicht über gemeinsam genutzte schreibgeschützte Daten informiert werden muss. Wenn der Mechanismus der gemeinsam genutzten Bibliothek jedoch nicht zusammenhängende Datenbereiche einführen kann, die möglicherweise Zeiger enthalten, muss der Kollektor informiert werden.
Bei den meisten Signalen kann die Signalverarbeitung während der Erfassung und während unterbrechungsfreier Teile des Zuordnungsprozesses verzögert werden. Wie bei Standard-ANSI-C-Mallocs ist es standardmäßig unsicher, Malloc (und andere GC-Routinen) von einem Signalhandler aus aufzurufen, während möglicherweise ein anderer Malloc-Aufruf ausgeführt wird.
Der Allokator/Kollektor kann auch für den Thread-sicheren Betrieb konfiguriert werden. (Eine vollständige Signalsicherheit kann ebenfalls erreicht werden, jedoch nur auf Kosten von zwei Systemaufrufen pro Malloc, was normalerweise inakzeptabel ist.)
WARNUNG : Der Collector garantiert nicht, den Thread-lokalen Speicher zu scannen (z. B. von der Art, auf die mit pthread_getspecific
zugegriffen wird). Der Kollektor scannt jedoch Thread-Stapel. Daher besteht die beste Lösung im Allgemeinen darin, sicherzustellen, dass alle im Thread-lokalen Speicher gespeicherten Zeiger für die Dauer ihrer Lebensdauer auch auf dem Stapel des Threads gespeichert werden. (Dies ist wohl ein seit langem bestehender Fehler, der jedoch noch nicht behoben wurde.)
Es gibt mehrere Möglichkeiten, den Kollektor zu erstellen:
CMake (dies ist die empfohlene Methode)
GNU autoconf/automake
Zick (experimentell)
MS nmake (direkt)
Makefile.direct
Manuelle C-Kompilierung
Der einfachste Weg, libgc (sowie libcord) zu erstellen und die Tests mit cmake auszuführen:
mkdir outcd out cmake -Dbuild_tests=ON .. cmake --build .ctest
Dies ist die plattformübergreifendste Art, die Bibliothek aufzubauen. Weitere Informationen finden Sie in README.cmake.
Bitte beachten Sie, dass das Collector-Quell-Repository keine configure
und ähnliche automatisch generierte Dateien enthält. Daher könnte der vollständige Vorgang der Autoconf-basierten Erstellung des Collectors aus dem Quell-Repository wie folgt aussehen:
./autogen.sh ./konfigurieren Scheck machen
Der Erstellungsprozess im GNU-Stil versteht die üblichen Ziele und Optionen. make install
installiert libgc und libcord. Versuchen Sie ./configure --help
, um alle Konfigurationsoptionen anzuzeigen. Derzeit ist es nicht möglich, alle Kombinationen von Build-Optionen auf diese Weise auszuüben.
Weitere Informationen finden Sie in README.autoconf.
Der Aufbau und Test des Kollektors mit Zig ist in seiner einfachsten Form unkompliziert:
Zick-Build-Test
Es ist möglich, den Build mithilfe von Variablen zu konfigurieren, z. B. zig build -Denable_redirect_malloc -Denable_threads=false
. Zig bietet hervorragende Cross-Compilation-Funktionalität und ist wie folgt konfigurierbar:
zig build -Dtarget=riscv64-linux-musl
Derzeit ist eine Nightly-Version von Zig 0.12 erforderlich, die unter https://ziglang.org/download/ heruntergeladen werden kann.
Unter Windows ist es, sofern die Microsoft-Build-Tools installiert und entsprechend konfiguriert sind, möglich, die Bibliothek zu erstellen und die Tests direkt mit nmake
auszuführen, z. B. durch Eingabe von nmake -f NT_MAKEFILE check
. Die empfohlene Methode ist jedoch die Verwendung von cmake wie oben beschrieben.
Weitere Informationen finden Sie in README.win32.
Für den altmodischen (klassischen) Makefile-basierten Build-Prozess führt die Eingabe make -f Makefile.direct check
automatisch zur Erstellung von libgc und libcord und führt dann eine Reihe von Tests aus, z. B. gctest
. Der Test ist ein etwas oberflächlicher Test der Kollektorfunktionalität. Ein Fehler wird durch einen Core-Dump oder eine Meldung angezeigt, dass der Collector defekt ist. Die Ausführung gctest
kann auf vernünftigen 64-Bit-Desktops der Generation 2023 ein Dutzend Sekunden dauern. Es kann bis zu 30 MB Speicher beanspruchen.
Makefile.direct generiert eine Bibliothek libgc.a, mit der Sie eine Verknüpfung herstellen sollten.
Schließlich könnte der Kollektor auf den meisten Zielen direkt mit einem einzigen Compiler-Aufruf erstellt und getestet werden, wie folgt (im Beispiel fehlt die Unterstützung für Multithreading):
cc -I füge -o gctest tests/gctest.c extra/gc.c && ./gctest hinzu
Dies könnte beispielsweise für Debugging-Zwecke praktisch sein.
Die Bibliothek kann während des Builds genauer konfiguriert werden, indem die in der Datei README.macros aufgeführten Makros definiert werden.
Die Bibliothek wird standardmäßig mit aktivierter Thread-Unterstützung (d. h. für threadsicheren Betrieb) erstellt, sofern sie nicht ausdrücklich deaktiviert wird durch:
-Denable_threads=false
-Option, die an cmake
oder zig build
übergeben wird
--disable-threads
Option, die an ./configure
übergeben wird
Der Collector arbeitet in der Standardkonfiguration im Hintergrund. Im Falle von Problemen kann dies normalerweise durch Definieren der Umgebungsvariablen GC_PRINT_STATS
oder GC_PRINT_VERBOSE_STATS
geändert werden. Dies führt zu einigen Zeilen beschreibender Ausgabe für jede Sammlung. (Die angegebenen Statistiken weisen einige Besonderheiten auf. Aus verschiedenen Gründen scheinen die Dinge nicht zu stimmen, vor allem aufgrund von Fragmentierungsverlusten. Diese sind für das künstliche Programm gctest
wahrscheinlich viel bedeutender als für Ihre Anwendung.)
Die Verwendung (Klonen) von libatomic_ops
ist jetzt optional, sofern der Compiler atomare intrinsische Funktionen unterstützt. Die meisten modernen Compiler tun dies. Die bemerkenswerte Ausnahme ist der MS-Compiler (ab Visual Studio 2022).
Bei Bedarf verfügen die meisten Betriebssystemdistribute über das Paket libatomic_ops
; Alternativ können Sie es aus dem Space https://github.com/ivmai/libatomic_ops herunterladen oder klonen.
Der Collector ist derzeit so konzipiert, dass er im Wesentlichen unverändert auf Computern läuft, die einen flachen 32-Bit- oder 64-Bit-Adressraum verwenden. Dazu gehören die überwiegende Mehrheit der Workstations und x86-PCs (i386 oder höher).
In einigen Fällen (z. B. OS/2, Win32) wird ein separates Makefile bereitgestellt; Für diese gibt es eine separate hostspezifische Datei docs/platforms/README.*.
Dynamische Bibliotheken werden nur unter SunOS/Solaris (und selbst diese Unterstützung ist in der letzten Version von Sun 3 nicht funktionsfähig), Linux, FreeBSD, NetBSD, IRIX, HP/UX, Win32 (nicht win32s) und OSF/1 unter DEC vollständig unterstützt AXP-Maschinen und vielleicht ein paar andere, die oben in dyn_load.c aufgeführt sind. Auf anderen Maschinen empfehlen wir Ihnen, einen der folgenden Schritte auszuführen:
Fügen Sie Unterstützung für dynamische Bibliotheken hinzu (und senden Sie uns den Code).
Verwenden Sie statische Versionen der Bibliotheken.
Sorgen Sie dafür, dass dynamische Bibliotheken das Standard-Malloc verwenden. Dies ist immer noch gefährlich, wenn die Bibliothek einen Zeiger auf ein Garbage-Collected-Objekt speichert. Aber fast alle Standardschnittstellen verbieten dies, da sie korrekt mit Zeigern auf stapelzugeordnete Objekte umgehen. ( strtok
ist eine Ausnahme. Verwenden Sie es nicht.)
In allen Fällen gehen wir davon aus, dass die Zeigerausrichtung mit der von den Standard-C-Compilern erzwungenen Ausrichtung übereinstimmt. Wenn Sie einen nicht standardmäßigen Compiler verwenden, müssen Sie möglicherweise die in include/private/gc_priv.h
definierten Ausrichtungsparameter anpassen. Beachten Sie, dass dies auch bei gepackten Datensätzen/Strukturen ein Problem sein kann, wenn diese eine geringere Ausrichtung für Zeiger erzwingen.
Ein Port zu einer Maschine, die nicht byteadressiert ist oder keine 32-Bit- oder 64-Bit-Adressen verwendet, erfordert einen großen Aufwand. Eine Portierung auf einfaches MSDOS oder Win16 ist schwierig.
Für Maschinen, die noch nicht erwähnt wurden, oder für nicht standardmäßige Compiler werden hier einige Portierungsvorschläge bereitgestellt.
Die folgenden Routinen sind für den direkten Aufruf durch den Benutzer vorgesehen. Beachten Sie, dass normalerweise nur GC_malloc
erforderlich ist. GC_clear_roots
und GC_add_roots
Aufrufe können erforderlich sein, wenn der Collector eine Verfolgung von nicht standardmäßigen Stellen durchführen muss (z. B. von dynamischen Bibliotheksdatenbereichen auf einem Computer, auf dem der Collector sie noch nicht versteht). Auf einigen Computern kann es wünschenswert sein, GC_stackbottom
auf zu setzen eine gute Annäherung an die Stapelbasis (unten).
Der Clientcode kann gc.h
enthalten, das alle folgenden und viele andere definiert.
GC_malloc(bytes)
– Ordnen Sie ein Objekt einer bestimmten Größe zu. Im Gegensatz zu malloc wird das Objekt gelöscht, bevor es an den Benutzer zurückgegeben wird. GC_malloc
ruft den Garbage Collector auf, wenn es dies für angemessen hält. GC_malloc gibt möglicherweise 0 zurück, wenn es nicht in der Lage ist, ausreichend Speicherplatz vom Betriebssystem zu erhalten. Dies ist die wahrscheinlichste Folge von Platzmangel. Weitere mögliche Folgen sind, dass ein Funktionsaufruf aufgrund von fehlendem Stapelplatz fehlschlägt, oder dass der Kollektor auf andere Weise ausfällt, weil er seine internen Datenstrukturen nicht verwalten kann, oder dass ein wichtiger Systemprozess ausfällt und die Maschine lahmlegt. Die meisten dieser Möglichkeiten sind unabhängig von der Malloc-Implementierung.
GC_malloc_atomic(bytes)
– Ordnen Sie ein Objekt einer bestimmten Größe zu, das garantiert keine Zeiger enthält. Es kann nicht garantiert werden, dass das zurückgegebene Objekt gelöscht wird. (Kann immer durch GC_malloc
ersetzt werden, führt aber zu schnelleren Erfassungszeiten. Der Kollektor wird wahrscheinlich schneller ausgeführt, wenn große Zeichenarrays usw. mit GC_malloc_atomic
zugewiesen werden, als wenn sie statisch zugewiesen werden.)
GC_realloc(object, new_bytes)
– Ändern Sie die Größe des Objekts auf eine bestimmte Größe. Gibt einen Zeiger auf das neue Objekt zurück, der mit dem Zeiger auf das alte Objekt identisch sein kann oder auch nicht. Das neue Objekt wird genau dann als atomar angesehen, wenn das alte es war. Wenn das neue Objekt zusammengesetzt und größer als das ursprüngliche Objekt ist, werden die neu hinzugefügten Bytes gelöscht. Es ist sehr wahrscheinlich, dass dadurch ein neues Objekt zugewiesen wird.
GC_free(object)
– Geben Sie die Zuordnung eines von GC_malloc
oder GC_malloc_atomic
oder Freunden zurückgegebenen Objekts explizit frei. Nicht erforderlich, kann aber verwendet werden, um Sammlungen zu minimieren, wenn die Leistung von entscheidender Bedeutung ist. Wahrscheinlich ein Leistungsverlust für sehr kleine Objekte (<= 8 Bytes).
GC_expand_hp(bytes)
– Heap-Größe explizit erhöhen. (Dies erfolgt normalerweise automatisch, wenn eine Speicherbereinigung nicht genügend Speicher zurückgewinnen konnte. Explizite Aufrufe von GC_expand_hp
können unnötig häufige Sammlungen beim Programmstart verhindern.)
GC_malloc_ignore_off_page(bytes)
– Identisch mit GC_malloc
, aber der Client verspricht, einen Zeiger auf irgendwo innerhalb des ersten GC-Heap-Blocks (512 .. 4096 Bytes oder sogar mehr, je nach Konfiguration) des Objekts zu behalten, während es aktiv ist. (Dieser Zeiger sollte normalerweise als flüchtig deklariert werden, um Störungen durch Compiler-Optimierungen zu verhindern.) Dies ist die empfohlene Methode zum Zuweisen von allem, das wahrscheinlich größer als etwa 100 KB ist. ( GC_malloc
kann dazu führen, dass die Rückforderung solcher Objekte fehlschlägt.)
GC_set_warn_proc(proc)
– Kann verwendet werden, um Warnungen vom Collector umzuleiten. Solche Warnungen sollten selten sein und während der Codeentwicklung nicht ignoriert werden.
GC_enable_incremental()
– Ermöglicht die generative und inkrementelle Sammlung. Nützlich für große Heaps auf Computern, die Zugriff auf schmutzige Seiteninformationen ermöglichen. Einige Dirty-Bit-Implementierungen können das Debuggen beeinträchtigen (durch das Abfangen von Adressfehlern) und Beschränkungen für Heap-Argumente für Systemaufrufe auferlegen (da Schreibfehler innerhalb eines Systemaufrufs möglicherweise nicht gut behandelt werden).
GC_register_finalizer(object, proc, data, 0, 0)
und Freunde – Ermöglicht die Registrierung des Finalisierungscodes. Der vom Benutzer bereitgestellte Finalisierungscode ( (*proc)(object, data)
) wird aufgerufen, nachdem das Objekt nicht mehr erreichbar ist. Für anspruchsvollere Anwendungen und Probleme bei der Finalisierungsreihenfolge siehe gc.h
.
Die globale Variable GC_free_space_divisor
kann von ihrem Standardwert 3 nach oben angepasst werden, um weniger Speicherplatz und mehr Erfassungszeit zu verbrauchen, oder nach unten, um den gegenteiligen Effekt zu erzielen. Wenn Sie den Wert auf 1 setzen, werden Sammlungen fast deaktiviert und alle Zuweisungen führen dazu, dass der Heap einfach wächst.
Die Variable GC_non_gc_bytes
, die normalerweise 0 ist, kann geändert werden, um die von den oben genannten Routinen zugewiesene Speichermenge widerzuspiegeln, die nicht als Kandidat für die Sammlung in Betracht gezogen werden sollte. Eine unvorsichtige Verwendung kann natürlich zu einem übermäßigen Speicherverbrauch führen.
Durch die oben in include/private/gc_priv.h
definierten Parameter ist eine zusätzliche Optimierung möglich.
Wenn nur GC_malloc
verwendet werden soll, kann es sinnvoll sein, Folgendes zu definieren:
#define malloc(n) GC_malloc(n) #define calloc(m,n) GC_malloc((m)*(n))
Für kleine Teile SEHR allokationsintensiven Codes enthält gc_inline.h
einige Allokationsmakros, die anstelle von GC_malloc
und Co. verwendet werden können.
Alle extern sichtbaren Namen im Garbage Collector beginnen mit GC_
. Um Namenskonflikte zu vermeiden, sollte Clientcode dieses Präfix vermeiden, außer beim Zugriff auf Garbage Collector-Routinen.
Es gibt Bestimmungen zur Zuordnung mit expliziten Typinformationen. Dies ist selten notwendig. Details finden Sie in gc_typed.h
.
Die Ellis-Hull C++-Schnittstelle zum Collector ist in der Collector-Distribution enthalten. Wenn Sie dies verwenden möchten, geben Sie ./configure --enable-cplusplus && make
ein (oder cmake -Denable_cplusplus=ON . && cmake --build .
oder make -f Makefile.direct c++
je nachdem, welches Build-System Sie verwenden). Dadurch werden die Dateien libgccpp.a und libgctba.a oder ihre gemeinsam genutzten Bibliotheksäquivalente (libgccpp.so und libgctba.so) erstellt. Sie sollten entweder mit dem ersten (gccpp) oder dem zweiten (gctba) verknüpfen, aber nicht mit beiden. Die Definition der Schnittstelle finden Sie unter gc_cpp.h
und hier. Diese Schnittstelle versucht, den C++-Garbage-Collection-Vorschlag von Ellis-Detlefs ohne Compileränderungen anzunähern.
Sehr oft wird es auch notwendig sein, gc_allocator.h
und den dort deklarierten Allocator zum Aufbau von STL-Datenstrukturen zu verwenden. Andernfalls werden Unterobjekte von STL-Datenstrukturen mithilfe eines Systemzuweisers zugewiesen, und Objekte, auf die sie verweisen, werden möglicherweise vorzeitig erfasst.
Der Collector kann verwendet werden, um Lecks in C-Programmen aufzuspüren, die mit malloc/free ausgeführt werden sollen (z. B. Code mit extremen Echtzeit- oder Portabilitätsbeschränkungen). Definieren Sie dazu FIND_LEAK
im Makefile. Dies führt dazu, dass der Collector immer dann eine für Menschen lesbare Objektbeschreibung ausgibt, wenn ein unzugängliches Objekt gefunden wird, das nicht explizit freigegeben wurde. Auch solche Objekte werden automatisch zurückgefordert.
Wenn alle Objekte mit GC_DEBUG_MALLOC
zugewiesen werden (siehe nächster Abschnitt), enthält die menschenlesbare Objektbeschreibung standardmäßig mindestens die Quelldatei und die Zeilennummer, in der das durchgesickerte Objekt zugewiesen wurde. Dies kann manchmal ausreichend sein. (Auf einigen Maschinen wird auch ein kryptischer Stack-Trace gemeldet. Wenn dies nicht symbolisch ist, kann manchmal ein symbolischer Stack-Trace aufgerufen werden, indem das Programm „foo“ mit tools/callprocs.sh foo
aufgerufen wird. Es handelt sich um eine kurze Shell Skript, das adb aufruft, um Programmzählerwerte auf symbolische Adressen zu erweitern. Es wurde größtenteils von Scott Schwartz bereitgestellt.)
Beachten Sie, dass die im nächsten Abschnitt beschriebenen Debugging-Funktionen im Lecksuchmodus manchmal etwas WENIGER effektiv sein können, da GC_debug_free
im letzteren Modus tatsächlich zur Wiederverwendung des Objekts führt. (Andernfalls wird das Objekt einfach als ungültig markiert.) Beachten Sie außerdem, dass die meisten GC-Tests nicht für die sinnvolle Ausführung im FIND_LEAK
-Modus ausgelegt sind.
Die Routinen GC_debug_malloc
, GC_debug_malloc_atomic
, GC_debug_realloc
und GC_debug_free
bieten eine alternative Schnittstelle zum Collector, die bei Speicherüberschreibfehlern und Ähnlichem Hilfe bietet. Auf diese Weise zugeordnete Objekte werden mit zusätzlichen Informationen versehen. Einige dieser Informationen werden während der Speicherbereinigung überprüft und erkannte Inkonsistenzen werden an stderr gemeldet.
Einfache Fälle des Schreibens über das Ende eines zugewiesenen Objekts hinaus sollten abgefangen werden, wenn die Zuordnung des Objekts explizit aufgehoben wird oder wenn der Collector aufgerufen wird, während das Objekt aktiv ist. Bei der ersten Freigabe eines Objekts werden die mit einem Objekt verknüpften Debugging-Informationen gelöscht, sodass versehentlich wiederholte Aufrufe von GC_debug_free
die Freigabe eines Objekts ohne Debugging-Informationen melden. Fehler wegen unzureichendem Arbeitsspeicher werden zusätzlich zur Rückgabe NULL
an stderr gemeldet.
GC_debug_malloc
-Prüfung während der Garbage Collection wird mit dem ersten Aufruf dieser Funktion aktiviert. Dies führt zu einer gewissen Verlangsamung während der Sammlung. Wenn häufige Heap-Überprüfungen gewünscht sind, kann dies durch den expliziten Aufruf GC_gcollect
erreicht werden, z. B. über den Debugger.
GC_debug_malloc
zugewiesene Objekte sollten nicht an GC_realloc
oder GC_free
übergeben werden und umgekehrt. Es ist jedoch akzeptabel, nur einige Objekte mit GC_debug_malloc
zuzuweisen und GC_malloc
für andere Objekte zu verwenden, vorausgesetzt, die beiden Pools bleiben unterschiedlich. In diesem Fall besteht eine sehr geringe Wahrscheinlichkeit, dass GC_malloc
zugewiesene Objekte fälschlicherweise als überschrieben identifiziert werden. Dies sollte mit einer Wahrscheinlichkeit von höchstens eins zu 2**32 passieren. Diese Wahrscheinlichkeit ist Null, wenn GC_debug_malloc
nie aufgerufen wird.
GC_debug_malloc
, GC_debug_malloc_atomic
und GC_debug_realloc
nehmen zwei zusätzliche abschließende Argumente an, einen String und eine Ganzzahl. Diese werden vom Allokator nicht interpretiert. Sie werden im Objekt gespeichert (der String wird nicht kopiert). Wenn ein Fehler im Zusammenhang mit dem Objekt festgestellt wird, werden sie gedruckt.
Die Makros GC_MALLOC
, GC_MALLOC_ATOMIC
, GC_REALLOC
, GC_FREE
, GC_REGISTER_FINALIZER
und Freunde werden ebenfalls bereitgestellt. Diese erfordern dieselben Argumente wie die entsprechenden (nicht debuggenden) Routinen. Wenn gc.h
in der Definition von GC_DEBUG
enthalten ist, rufen sie die Debugging-Versionen dieser Funktionen auf und übergeben gegebenenfalls den aktuellen Dateinamen und die Zeilennummer als zwei zusätzliche Argumente. Wenn gc.h
eingefügt wird, ohne GC_DEBUG
definiert ist, werden alle diese Makros stattdessen als ihre Nicht-Debugging-Äquivalente definiert. ( GC_REGISTER_FINALIZER
ist erforderlich, da Zeiger auf Objekte mit Debugging-Informationen tatsächlich Zeiger auf eine Verschiebung von 16 Bytes vom Objektanfang sind und eine gewisse Übersetzung erforderlich ist, wenn Finalisierungsroutinen aufgerufen werden. Einzelheiten dazu, was im Header gespeichert ist, finden Sie in der Definition vom Typ oh in der Datei dbg_mlc.c.)
Normalerweise unterbricht der Collector den Clientcode für die Dauer einer Garbage-Collection-Markierungsphase. Dies kann inakzeptabel sein, wenn für Programme mit großen Heaps eine interaktive Reaktion erforderlich ist. Der Collector kann auch in einem „Generierungsmodus“ ausgeführt werden, in dem er normalerweise versucht, nur Objekte zu sammeln, die seit der letzten Garbage Collection zugewiesen wurden. Darüber hinaus werden Garbage Collections in diesem Modus größtenteils inkrementell ausgeführt, wobei ein kleiner Arbeitsaufwand als Reaktion auf jede große Anzahl von GC_malloc
Anforderungen ausgeführt wird.
Dieser Modus wird durch einen Aufruf von GC_enable_incremental
aktiviert.
Die inkrementelle und generationsbezogene Sammlung ist nur dann wirksam bei der Verkürzung der Pausenzeiten, wenn der Sammler auf irgendeine Weise erkennen kann, welche Objekte oder Seiten kürzlich geändert wurden. Der Kollektor nutzt zwei Informationsquellen:
Vom VM-System bereitgestellte Informationen. Dies kann in einer von mehreren Formen erfolgen. Unter Solaris 2.X (und möglicherweise auch unter anderen ähnlichen Systemen) können Informationen zu fehlerhaften Seiten aus dem /proc-Dateisystem gelesen werden. Unter anderen Systemen (z. B. SunOS4.X) ist es möglich, den Heap mit einem Schreibschutz zu versehen und die daraus resultierenden Fehler abzufangen. Auf diesen Systemen ist es erforderlich, dass Systemaufrufe, die in den Heap schreiben (außer Lesevorgänge), speziell vom Clientcode gehandhabt werden. Weitere Informationen finden Sie unter os_dep.c
.
Vom Programmierer bereitgestellte Informationen. Das Objekt gilt nach einem Aufruf von GC_end_stubborn_change
als fehlerhaft, sofern die Bibliothek ordnungsgemäß kompiliert wurde. Bei kurzlebigen Objekten lohnt sich der Einsatz in der Regel nicht. Beachten Sie, dass Fehler, die durch einen fehlenden GC_end_stubborn_change
oder GC_reachable_here
-Aufruf verursacht werden, wahrscheinlich sehr selten beobachtet werden und schwer zu verfolgen sind.
Jeder Speicher, der keinen erkennbaren Zeiger darauf hat, wird zurückgefordert. Das Exklusiv-Oder-Verknüpfen von Vorwärts- und Rückwärtslinks in einer Liste reicht nicht aus.
Einige C-Optimierer verlieren aufgrund geschickter Optimierungen möglicherweise den letzten unverstellten Zeiger auf ein Speicherobjekt. Dies wurde in der Praxis fast nie beobachtet.
Dies ist kein Echtzeit-Sammler. In der Standardkonfiguration sollte der Prozentsatz der für die Sammlung erforderlichen Zeit über alle Heap-Größen hinweg konstant sein. Bei größeren Heaps werden die Sammelpausen jedoch länger. Sie nehmen mit der Anzahl der Prozessoren ab, wenn die parallele Markierung aktiviert ist.
(Auf Maschinen aus dem Jahr 2007 können die GC-Zeiten in der Größenordnung von 5 ms pro MB zugänglichem Speicher liegen, der gescannt und verarbeitet werden muss. Ihr Kilometerstand kann variieren.) Die inkrementelle/generationelle Erfassungsfunktion kann in einigen Fällen hilfreich sein.
Bitte beziehen Sie sich auf Fehlerberichte und Ideen für neue Funktionen auf GitHub-Probleme. Bitte prüfen Sie vor der Einreichung, ob dies nicht bereits von jemand anderem erfolgt ist.
Wenn Sie einen Beitrag leisten möchten, senden Sie eine Pull-Anfrage an GitHub. Bitte verarbeiten Sie die geänderten Dateien vor der Übermittlung im Clang-Format.
Wenn Sie Hilfe benötigen, verwenden Sie Stack Overflow. Ältere technische Diskussionen sind im Archiv bdwgc
Mailingliste verfügbar – es kann als komprimierte Datei heruntergeladen oder bei Narkive durchsucht werden.
Um Ankündigungen zu neuen Veröffentlichungen zu erhalten, abonnieren Sie den RSS-Feed. (Um die Benachrichtigungen per E-Mail zu erhalten, kann ein kostenloser Drittanbieterdienst wie der IFTTT-RSS-Feed eingerichtet werden.) Um bei allen Problemen benachrichtigt zu werden, sehen Sie sich bitte das Projekt auf GitHub an.
Unser Ziel ist es, die Verwendung von bdwgc (libgc) sowohl in freier als auch in proprietärer Software zu vereinfachen. Daher unterliegt der konservative Boehm-Demers-Weiser-Garbage-Collector-Code, von dem wir erwarten, dass er dynamisch oder statisch in eine Client-Anwendung eingebunden wird, einer eigenen Lizenz, die im Geiste einer MIT-Lizenz ähnelt.
Die genauen Lizenzinformationen finden Sie in der Datei LICENSE.
Alle Mitwirkenden sind in der AUTHORS-Datei aufgeführt.