Garbage Collection

Xamarin.Android verwendet den Garbage Collector für die einfache Generation von Mono. Dies ist ein Mark-and-Sweep Garbage Collector mit zwei Generationen und einem großen Objektbereich mit zwei Arten von Auflistungen:

  • Kleinere Sammlungen (sammelt Gen0 Heap)
  • Hauptauflistungen (sammelt Heaps für gen1- und große Objekträume).

Hinweis

Fehlt eine explizite Sammlung über GC. Sammlungssammlungen sind bei Bedarf auf Der Grundlage von Heap-Zuordnungen. Dies ist kein Referenzzählsystem. Objekte werden nicht erfasst, sobald keine offenen Verweise vorhanden sind oder wenn ein Bereich beendet wurde. Die GC wird ausgeführt, wenn der kleinere Heap nicht genügend Arbeitsspeicher für neue Zuordnungen hat. Wenn keine Zuordnungen vorhanden sind, wird sie nicht ausgeführt.

Kleinere Sammlungen sind billig und häufig und werden verwendet, um kürzlich zugeordnete und tote Objekte zu sammeln. Kleinere Auflistungen werden nach allen wenigen MB zugeordneten Objekten ausgeführt. Kleinere Auflistungen können manuell durch Aufrufen von GC ausgeführt werden. Sammeln (0)

Hauptauflistungen sind teuer und weniger häufig und werden verwendet, um alle inaktiven Objekte zurückzufordern. Hauptauflistungen werden ausgeführt, sobald der Speicher für die aktuelle Heapgröße erschöpft ist (bevor die Größe des Heaps geändert wird). Hauptauflistungen können manuell durch Aufrufen von GC ausgeführt werden. Sammeln () oder durch Aufrufen von GC. Sammeln (int) mit dem Argument GC. MaxGeneration.

Auflistungen von VM-Objekten

Es gibt drei Kategorien von Objekttypen.

  • Verwaltete Objekte: Typen, die nicht von Java.Lang.Object erben, z. B. System.String. Diese werden normalerweise von der GC gesammelt.

  • Java-Objekte: Java-Typen, die innerhalb der Android-Laufzeit-VM vorhanden sind, aber nicht für die Mono-VM verfügbar gemacht werden. Dies ist langweilig und wird nicht weiter diskutiert. Diese werden normalerweise von der Android-Laufzeit-VM gesammelt.

  • Peerobjekte: Typen, die IJavaObject implementieren, z. B. alle Java.Lang.Object- und Java.Lang.Throwable-Unterklassen. Instanzen dieser Typen verfügen über zwei "Hälften" eines verwalteten Peers und eines systemeigenen Peers. Der verwaltete Peer ist eine Instanz der C#-Klasse. Der systemeigene Peer ist eine Instanz einer Java-Klasse innerhalb der Android-Laufzeit-VM, und die C# IJavaObject.Handle-Eigenschaft enthält einen globalen JNI-Verweis auf den systemeigenen Peer.

Es gibt zwei Arten von systemeigenen Peers:

  • Framework-Peers : "Normale" Java-Typen, die nichts von Xamarin.Android kennen, z. B. android.content.Context.

  • Benutzer-Peers : Android-Aufrufbare Wrapper , die zur Buildzeit für jede Java.Lang.Object-Unterklasse in der Anwendung generiert werden.

Da es zwei VMs innerhalb eines Xamarin.Android-Prozesses gibt, gibt es zwei Arten von Garbage Collections:

  • Android-Laufzeitsammlungen
  • Mono-Auflistungen

Android-Laufzeitsammlungen funktionieren normal, aber mit einer Einschränkung: Ein globaler JNI-Verweis wird als GC-Stamm behandelt. Wenn also ein globaler JNI-Verweis vorhanden ist, der ein VM-Objekt für Android-Runtime enthält, kann das Objekt nicht erfasst werden, auch wenn es andernfalls zur Sammlung berechtigt ist.

Mono-Sammlungen sind der Ort, an dem der Spaß passiert. Verwaltete Objekte werden normal erfasst. Peerobjekte werden gesammelt, indem sie den folgenden Prozess ausführen:

  1. Alle Peer-Objekte, die für die Mono-Auflistung berechtigt sind, haben ihren globalen JNI-Verweis durch einen schwachen globalen JNI-Verweis ersetzt.

  2. Eine VM GC für Android-Runtime wird aufgerufen. Jede native Peerinstanz kann gesammelt werden.

  3. Die in (1) erstellten schwachen globalen JNI-Verweise werden überprüft. Wenn der schwache Verweis erfasst wurde, wird das Peer-Objekt erfasst. Wenn der schwache Verweis nicht erfasst wurde, wird der schwache Verweis durch einen globalen JNI-Verweis ersetzt, und das Peer-Objekt wird nicht erfasst. Hinweis: Bei API 14+ bedeutet dies, dass sich der zurückgegebene IJavaObject.Handle Wert nach einer GC ändern kann.

Das Endergebnis ist, dass eine Instanz eines Peer-Objekts so lange lebt, wie von verwaltetem Code (z. B. in einer static Variablen gespeichert) oder von Java-Code referenziert wird. Darüber hinaus wird die Lebensdauer von nativen Peers über das hinausgehen, was sie sonst leben würden, da der native Peer erst gesammelt werden kann, wenn sowohl der native Peer als auch der verwaltete Peer gesammelt werden können.

Objektzyklen

Peerobjekte sind logisch sowohl innerhalb der Android-Laufzeit als auch innerhalb der Mono-VM vorhanden. Beispielsweise verfügt eine verwaltete Android.App.Activity-Peerinstanz über eine entsprechende android.app.Activity Framework-Peer-Java-Instanz. Es kann erwartet werden, dass alle Objekte, die von Java.Lang.Object erben, innerhalb beider VMs Darstellungen aufweisen.

Alle Objekte mit Darstellung in beiden VMs verfügen über Lebensdauern, die im Vergleich zu Objekten erweitert werden, die nur innerhalb einer einzelnen VM vorhanden sind (z. B. a System.Collections.Generic.List<int>). Aufrufen von GC. Das Sammeln erfasst diese Objekte nicht unbedingt, da die Xamarin.Android GC sicherstellen muss, dass das Objekt nicht von beiden virtuellen Computern referenziert wird, bevor es erfasst wird.

Um die Objektlebensdauer zu verkürzen, sollte Java.Lang.Object.Dispose() aufgerufen werden. Dadurch wird die Verbindung des Objekts zwischen den beiden virtuellen Computern manuell "getrennt", indem der globale Verweis freigegeben wird, sodass die Objekte schneller gesammelt werden können.

Automatische Auflistungen

Ab Release 4.1.0 führt Xamarin.Android automatisch eine vollständige GC durch, wenn ein Gref-Schwellenwert überschritten wird. Dieser Schwellenwert beträgt 90 % der bekannten maximalen Grefs für die Plattform: 1800 Grefs auf dem Emulator (2000 max) und 46800 Grefs auf Hardware (maximal 52000). Hinweis: Xamarin.Android zählt nur die Grefs, die von Android.Runtime.JNIEnv erstellt wurden, und weiß nicht über andere Grefs, die im Prozess erstellt wurden. Dies ist nur heuristisch.

Wenn eine automatische Auflistung ausgeführt wird, wird eine Meldung ähnlich wie im Debugprotokoll gedruckt:

I/monodroid-gc(PID): 46800 outstanding GREFs. Performing a full GC!

Das Auftreten dieses Ereignisses ist nicht deterministisch und kann zu inopportunen Zeiten auftreten (z. B. in der Mitte des Grafikrenderings). Wenn diese Meldung angezeigt wird, möchten Sie möglicherweise eine explizite Auflistung an anderer Stelle ausführen, oder Sie möchten versuchen, die Lebensdauer von Peerobjekten zu verringern.

GC-Brückenoptionen

Xamarin.Android bietet transparente Speicherverwaltung mit Android und der Android-Runtime. Sie wird als Erweiterung des Mono Garbage Collector namens GC Bridge implementiert.

Die GC-Brücke funktioniert während einer Mono Garbage Collection und stellt heraus, welche Peerobjekte ihre "Liveness" mit dem Android-Runtime-Heap überprüft. Die GC-Brücke bestimmt diese Bestimmung, indem Sie die folgenden Schritte ausführen (in der Reihenfolge):

  1. Führen Sie das Monoverweisdiagramm von nicht erreichbaren Peerobjekten in die java-Objekte ein, die sie darstellen.

  2. Führen Sie eine Java GC-Datei aus.

  3. Überprüfen Sie, welche Objekte wirklich tot sind.

Mit diesem komplizierten Prozess können Unterklassen beliebige Java.Lang.Object Objekte frei referenziert werden. Es entfernt alle Einschränkungen, welche Java-Objekte an C# gebunden werden können. Aufgrund dieser Komplexität kann der Brückenprozess sehr teuer sein und kann zu spürbaren Pausen in einer Anwendung führen. Wenn die Anwendung erhebliche Pausen hat, lohnt es sich, eine der folgenden drei GC Bridge-Implementierungen zu untersuchen:

  • Tarjan - Ein völlig neues Design der GC Bridge basierend auf Robert Tarjans Algorithmus und Rückwärtsvermehrung. Es hat die beste Leistung unter unseren simulierten Workloads, hat aber auch den größeren Anteil an experimentellem Code.

  • Neu - Eine wichtige Überholung des ursprünglichen Codes, das Beheben von zwei Instanzen des quadratischen Verhaltens, aber die Beibehaltung des Kernalgorithmus (basierend auf kosarajus Algorithmus für die Suche stark verbundene Komponenten).

  • Alt - Die ursprüngliche Implementierung (gilt als die stabilste der drei). Dies ist die Brücke, die eine Anwendung verwenden soll, wenn die GC_BRIDGE Pausen akzeptabel sind.

Die einzige Möglichkeit, herauszufinden, welche GC Bridge am besten funktioniert, besteht darin, in einer Anwendung zu experimentieren und die Ausgabe zu analysieren. Es gibt zwei Möglichkeiten, die Daten zum Benchmarking zu sammeln:

  • Protokollierung aktivieren – Aktivieren Sie die Protokollierung (wie im Abschnitt "Konfiguration" beschrieben) für jede GC-Brücke-Option, erfassen und vergleichen Sie dann die Protokollausgaben aus jeder Einstellung. Überprüfen Sie die GC Nachrichten für jede Option, insbesondere die GC_BRIDGE Nachrichten. Pausen bis zu 150 ms für nicht interaktive Anwendungen sind erträglich, aber Pausen über 60 ms für sehr interaktive Anwendungen (z. B. Spiele) sind ein Problem.

  • Brückenbuchhaltung aktivieren – Die Brückenbuchhaltung zeigt die durchschnittlichen Kosten der Objekte an, die von den einzelnen objekten, die am Brückenprozess beteiligt sind. Wenn Sie diese Informationen nach Größe sortieren, werden Hinweise dazu bereitgestellt, was die größte Menge zusätzlicher Objekte enthält.

Die Standardeinstellung ist Tarjan. Wenn Sie eine Regression finden, stellen Sie möglicherweise fest, dass diese Option auf " Alt" festgelegt werden muss. Außerdem können Sie die stabilere Alte Option verwenden, wenn Tarjan keine Verbesserung der Leistung erzeugt.

Um anzugeben, welche GC_BRIDGE Option eine Anwendung verwenden soll, übergeben bridge-implementation=newbridge-implementation=oldoder bridge-implementation=tarjan an die MONO_GC_PARAMS Umgebungsvariable. Dies erfolgt durch Hinzufügen einer neuen Datei zu Ihrem Projekt mit einer Buildaktion von AndroidEnvironment. Zum Beispiel:

MONO_GC_PARAMS=bridge-implementation=tarjan

Weitere Informationen finden Sie unter Konfiguration.

Helfen beim GC

Es gibt mehrere Möglichkeiten, die GC zu unterstützen, um die Speichernutzung und Die Sammlungszeiten zu reduzieren.

Löschen von Peerinstanzen

Die GC verfügt über eine unvollständige Ansicht des Prozesses und kann nicht ausgeführt werden, wenn der Arbeitsspeicher niedrig ist, da die GC nicht weiß, dass der Arbeitsspeicher niedrig ist.

Beispielsweise beträgt eine Instanz eines Java.Lang.Object-Typs oder abgeleiteten Typs mindestens 20 Byte Größe (vorbehaltlich einer Änderung ohne Vorherige Ankündigung usw.). Verwaltete aufrufbare Wrapper fügen keine zusätzlichen Instanzmember hinzu. Wenn Sie also über eine Android.Graphics.Bitmap-Instanz verfügen, die sich auf einen 10 MB Arbeitsspeicher bezieht, weiß Xamarin.Android es GC nicht, dass – das GC sieht ein 20-Byte-Objekt und kann nicht feststellen, dass es mit Mit Android-Runtime-zugeordneten Objekten verknüpft ist, die 10 MB Arbeitsspeicher lebendig halten.

Es ist häufig notwendig, dem GC zu helfen. Leider, GC. AddMemoryPressure() und GC. RemoveMemoryPressure() wird nicht unterstützt. Wenn Sie also wissen, dass Sie gerade ein großes java-zugeordnetes Objektdiagramm freigegeben haben, müssen Sie MÖGLICHERWEISE GC manuell aufrufen. Collect() to prompt a GC to release the Java-side memory, or you can explicitly dispose of Java.Lang.Object subclasses, breaking the mapping between the managed callable wrapper and the Java instance.

Hinweis

Sie müssen beim Löschen von Java.Lang.Object Unterklasseninstanzen äußerst vorsichtig sein.

Um die Möglichkeit einer Speicherbeschädigung zu minimieren, beachten Sie beim Aufrufen Dispose()die folgenden Richtlinien.

Gemeinsame Nutzung zwischen mehreren Threads

Wenn die Java- oder verwaltete Instanz möglicherweise zwischen mehreren Threads geteilt wird, sollte sie nie d sein.Dispose() Beispiel: Typeface.Create() kann eine zwischengespeicherte Instanz zurückgeben. Wenn mehrere Threads dieselben Argumente bereitstellen, erhalten sie dieselbe Instanz. Dispose()Folglich kann die Typeface Ing der Instanz von einem Thread andere Threads ungültig werden, was zu s von JNIEnv.CallVoidMethod() (u. a.) führen ArgumentExceptionkann, da die Instanz aus einem anderen Thread entfernt wurde.

Entfernen gebundener Java-Typen

Wenn es sich bei der Instanz um einen gebundenen Java-Typ handelt, kann die Instanz verworfen werden, solange die Instanz nicht aus verwaltetem Code wiederverwendet wird und die Java-Instanz nicht für Threads freigegeben werden kann (siehe vorherige Typeface.Create() Diskussion). (Diese Entscheidung zu treffen, kann schwierig sein.) Wenn die Java-Instanz das nächste Mal verwalteten Code eingibt, wird dafür ein neuer Wrapper erstellt.

Dies ist häufig hilfreich, wenn es um Drawables und andere ressourcenintensive Instanzen geht:

using (var d = Drawable.CreateFromPath ("path/to/filename"))
    imageView.SetImageDrawable (d);

Die obige Methode ist sicher, da der von Drawable.CreateFromPath() zurückgegebene Peer auf einen Framework-Peer und nicht auf einen Benutzer-Peer verweist. Der Dispose() Aufruf am Ende des using Blocks unterbricht die Beziehung zwischen den verwalteten Drawable- und Framework Drawable-Instanzen, sodass die Java-Instanz erfasst werden kann, sobald die Android-Laufzeit erforderlich ist. Dies wäre nicht sicher, wenn peerinstanz auf einen Benutzer-Peer verwiesen wurde. Hier verwenden wir "externe" Informationen, um zu wissen , dass der Drawable Anruf nicht auf einen Benutzer-Peer verweisen kann und somit der Dispose() Anruf sicher ist.

Entfernen anderer Typen

Wenn sich die Instanz auf einen Typ bezieht, der keine Bindung eines Java-Typs ist (z. B. ein benutzerdefinierter Activity), RUFEN SIE NICHT auf Dispose() , es sei denn, Sie wissen , dass kein Java-Code überschriebene Methoden für diese Instanz aufruft. Wenn dies nicht der Fehler ist, führt dies zu NotSupportedExceptions.

Wenn Sie beispielsweise einen benutzerdefinierten Klicklistener haben:

partial class MyClickListener : Java.Lang.Object, View.IOnClickListener {
    // ...
}

Sie sollten diese Instanz nicht verwerfen, da Java in Zukunft versuchen wird, Methoden darauf aufzurufen:

// BAD CODE; DO NOT USE
Button b = FindViewById<Button> (Resource.Id.myButton);
using (var listener = new MyClickListener ())
    b.SetOnClickListener (listener);

Verwenden expliziter Prüfungen zum Vermeiden von Ausnahmen

Wenn Sie eine Java.Lang.Object.Dispose-Überladungsmethode implementiert haben, vermeiden Sie das Berühren von Objekten, die JNI umfassen. Dies kann dazu führen, dass ein Double-Dispose-Fall entsteht, der es Ihrem Code ermöglicht (tödlich) versucht, auf ein zugrunde liegendes Java-Objekt zuzugreifen, das bereits garbage-collection wurde. Dadurch wird eine Ausnahme wie folgt erzeugt:

System.ArgumentException: 'jobject' must not be IntPtr.Zero.
Parameter name: jobject
at Android.Runtime.JNIEnv.CallVoidMethod

Diese Situation tritt häufig auf, wenn das erste Löschen eines Objekts bewirkt, dass ein Element null wird, und dann führt ein nachfolgender Zugriffsversuch für dieses NULL-Element dazu, dass eine Ausnahme ausgelöst wird. Insbesondere wird das Objekt Handle (das eine verwaltete Instanz mit seiner zugrunde liegenden Java-Instanz verknüpft) beim ersten Dispose ungültig, verwalteter Code versucht jedoch weiterhin, auf diese zugrunde liegende Java-Instanz zuzugreifen, obwohl es nicht mehr verfügbar ist (weitere Informationen zur Zuordnung zwischen Java-Instanzen und verwalteten Instanzen finden Sie unter Managed Callable Wrappers ).

Eine gute Möglichkeit, diese Ausnahme zu verhindern, besteht darin, in Ihrer Dispose Methode explizit zu überprüfen, ob die Zuordnung zwischen der verwalteten Instanz und der zugrunde liegenden Java-Instanz noch gültig ist. Das heißt, überprüfen Sie, ob das Objekt Handle null (IntPtr.Zero) ist, bevor auf seine Member zugegriffen wird. Die folgende Dispose Methode greift beispielsweise auf ein childViews Objekt zu:

class MyClass : Java.Lang.Object, ISomeInterface 
{
    protected override void Dispose (bool disposing)
    {
        base.Dispose (disposing);
        for (int i = 0; i < this.childViews.Count; ++i)
        {
            // ...
        }
    }
}

Wenn ein anfänglicher Dispose-Pass childViews eine ungültige HandleUrsache hat, löst der for Schleifenzugriff einen ArgumentException. Durch Hinzufügen einer expliziten Handle NULL-Prüfung vor dem ersten childViews Zugriff verhindert die folgende Dispose Methode, dass die Ausnahme auftritt:

class MyClass : Java.Lang.Object, ISomeInterface 
{
    protected override void Dispose (bool disposing)
    {
        base.Dispose (disposing);

        // Check for a null handle:
        if (this.childViews.Handle == IntPtr.Zero)
            return;

        for (int i = 0; i < this.childViews.Count; ++i)
        {
            // ...
        }
    }
}

Reduzieren von Referenzinstanzen

Wenn eine Instanz eines Typs oder einer Java.Lang.Object Unterklasse während der GC gescannt wird, muss auch das gesamte Objektdiagramm , auf das sich die Instanz bezieht, überprüft werden. Das Objektdiagramm ist der Satz von Objektinstanzen, auf die sich die "Stamminstanz" bezieht, sowie auf alle Elemente, auf die sich die Stamminstanz bezieht, rekursiv.

Betrachten Sie die folgende Klasse:

class BadActivity : Activity {

    private List<string> strings;

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        strings.Value = new List<string> (
                Enumerable.Range (0, 10000)
                .Select(v => new string ('x', v % 1000)));
    }
}

Wenn BadActivity das Objektdiagramm erstellt wird, enthält das Objektdiagramm 10004-Instanzen (1x BadActivity, 1x strings, 1x string[] gehalten von strings, 10000x-Zeichenfolgeninstanzen), die alle überprüft werden müssen, wenn die BadActivity Instanz gescannt wird.

Dies kann nachteilige Auswirkungen auf Ihre Sammlungszeiten haben, was zu erhöhten GC-Pausenzeiten führt.

Sie können der GC helfen, indem Sie die Größe von Objektdiagrammen verringern , die von Benutzer-Peerinstanzen verwurzelt sind. Im obigen Beispiel kann dies erfolgen, indem Sie zu einer separaten Klasse wechseln BadActivity.strings , die nicht von Java.Lang.Object erbt:

class HiddenReference<T> {

    static Dictionary<int, T> table = new Dictionary<int, T> ();
    static int idgen = 0;

    int id;

    public HiddenReference ()
    {
        lock (table) {
            id = idgen ++;
        }
    }

    ~HiddenReference ()
    {
        lock (table) {
            table.Remove (id);
        }
    }

    public T Value {
        get { lock (table) { return table [id]; } }
        set { lock (table) { table [id] = value; } }
    }
}

class BetterActivity : Activity {

    HiddenReference<List<string>> strings = new HiddenReference<List<string>>();

    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        strings.Value = new List<string> (
                Enumerable.Range (0, 10000)
                .Select(v => new string ('x', v % 1000)));
    }
}

Nebenauflistungen

Kleinere Auflistungen können manuell durch Aufrufen von GC ausgeführt werden. Collect(0). Kleinere Sammlungen sind billig (im Vergleich zu großen Sammlungen), haben aber erhebliche Fixkosten, daher möchten Sie sie nicht zu oft auslösen und sollten eine Pausenzeit von ein paar Millisekunden haben.

Wenn Ihre Anwendung über einen "Steuerzyklus" verfügt, bei dem dasselbe über und über ausgeführt wird, ist es möglicherweise ratsam, eine kleinere Sammlung manuell durchzuführen, sobald der Steuerzyklus beendet wurde. Beispiel für Steuerzyklen:

  • Der Renderingzyklus eines einzelnen Spielframes.
  • Die gesamte Interaktion mit einem bestimmten App-Dialogfeld (Öffnen, Ausfüllen, Schließen)
  • Eine Gruppe von Netzwerkanforderungen zum Aktualisieren/Synchronisieren von App-Daten.

Hauptsammlungen

Hauptauflistungen können manuell durch Aufrufen von GC ausgeführt werden. Collect() oder GC.Collect(GC.MaxGeneration).

Sie sollten selten ausgeführt werden und können beim Sammeln eines 512 MB-Heaps eine Pausenzeit von einer Sekunde auf einem Android-Gerät haben.

Hauptauflistungen sollten nur bei Bedarf manuell aufgerufen werden:

Diagnostik

Um nachzuverfolgen, wann globale Verweise erstellt und zerstört werden, können Sie die debug.mono.log Systemeigenschaft so festlegen, dass sie Gref und/oder Gc enthält.

Konfiguration

Der Xamarin.Android Garbage Collector kann durch Festlegen der MONO_GC_PARAMS Umgebungsvariable konfiguriert werden. Umgebungsvariablen können mit einer Build-Aktion von AndroidEnvironment festgelegt werden.

Die MONO_GC_PARAMS Umgebungsvariable ist eine durch Trennzeichen getrennte Liste der folgenden Parameter:

  • nursery-size = Größe : Legt die Größe der Kindergärten fest. Die Größe wird in Byte angegeben und muss eine Potenz von zwei sein. Die Suffixe km und g können verwendet werden, um Kilo-, Mega- und Gigabyte anzugeben. Die Kindergärten sind die erste Generation (von zwei). Eine größere Kindergärten beschleunigen in der Regel das Programm, verwenden aber offensichtlich mehr Arbeitsspeicher. Die Standard-Kindergärtengröße 512 kb.

  • soft-heap-limit = size : Die maximale verwaltete Speicherauslastung für die App. Wenn die Speichernutzung unter dem angegebenen Wert liegt, ist die GC für die Ausführungszeit optimiert (weniger Sammlungen). Oberhalb dieses Grenzwerts ist die GC für die Speicherauslastung (weitere Sammlungen) optimiert.

  • evacuation-threshold = Schwellenwert : Legt die Evakuierungsschwelle in Prozent fest. Der Wert muss eine ganze Zahl im Bereich von 0 bis 100 sein. Der Standardwert ist 66. Wenn die Aufräumphase der Sammlung feststellt, dass die Belegung eines bestimmten Heap-Blocktyps kleiner als dieser Prozentsatz ist, wird eine Kopiersammlung für diesen Blocktyp in der nächsten Hauptsammlung ausgeführt, wodurch die Belegung auf nahe 100 Prozent wiederhergestellt wird. Der Wert 0 deaktiviert die Evakuierung.

  • bridge-implementation = Bridge-Implementierung : Dadurch wird die GC Bridge-Option festgelegt, um GC-Leistungsprobleme zu beheben. Es gibt drei mögliche Werte: alt , neu , tarjan.

  • bridge-require-precise-merge: Die Tarjan-Brücke enthält eine Optimierung, die in seltenen Fällen dazu führen kann, dass ein Objekt ein GC gesammelt wird, nachdem es zum Ersten Müll wird. Durch Das Einschließen dieser Option wird diese Optimierung deaktiviert, wodurch GCs vorhersehbarer, aber potenziell langsamer werden.

Wenn Sie z. B. die GC so konfigurieren möchten, dass sie eine Heap-Größenbeschränkung von 128 MB hat, fügen Sie Ihrem Projekt eine neue Datei mit einer BuildaktionAndroidEnvironment mit dem Inhalt hinzu:

MONO_GC_PARAMS=soft-heap-limit=128m