2017-12-29 24 views
1

Wenn ich einige Daten über FFI zuordnen und einen Finalizer damit verbinden, bekomme ich eine ForeignPtr in Haskell. Wenn dieser Zeiger referenziert wird, erfasst GC den Zeiger, wodurch der Finalizer ausgeführt wird. Aber das Sammeln findet nur statt, wenn GC ausgeführt wurde und "Nicht-Referenzieren" GC nicht zum Ausführen zwingt. I.e. Es kann viele Zeiger herumliegen, aber da Zeiger selbst nicht viel Speicher belegen, sieht RTS einfach keinen Grund, GC zu starten, weil die Größe von fremden Daten nicht von der RTS verfolgt wird, gemäß meiner investigations. Ist das richtig?Fremde Daten und Garbage Collection

Wie kommuniziere ich "wenn dieser Zeiger nicht referenziert wird, sammeln Sie ihn sofort" an die RTS? Gibt es Flags, mit denen Sie steuern können, wann der GC gestartet wird? Ist das ein Problem für ein echtes Programm (da jedes echte Programm immer genug expliziten Müll hat, um GC zu stimulieren)?

+1

[Dieser Blogbeitrag] (http://www.tweag.io/posts/2017-11-29-linearine-jvm.html) spricht über dieses Problem. Es ist eine gute Zusammenfassung des gegenwärtigen Stands der Technik für diese Art von Dingen. – Alec

Antwort

3

Der RTS hat keine Ahnung, ob ein Teil der Daten referenziert wird, bis GC ausgeführt wird. GHC hat keine Referenzzählung GC, die eine sofortige Aktion auf Müll ermöglichen würde. Sie könnten versuchen, die Referenzzählung selbst durchzuführen, oder verwenden Sie den manuellen GC von System.Mem.

Ausländische Zuweisungen werden im Haskell-Land nicht erfasst. Wenn Sie mehr Kontrolle, aber keine benutzerdefinierte GC oder Referenzzählung wünschen, können Sie e. G. Foreign.Marhsal.Array für manuelle/bereichsweise Zuordnung und Freigabe.

Eine weitere Option ist die Verwendung der fixierten Zuweisung in GHC RTS. Dies gibt Ihnen Speicher, der nicht von GC bewegt wird. Verweise auf gepinnte Daten können ohne zusätzlichen Aufwand an fremden Code übergeben werden, gepinnte Daten werden jedoch nachverfolgt, können GC-d sein und lösen GC auf die gleiche Weise wie die üblichen Heap-Daten aus. Here's eine API für gepinnte Daten. Eine andere Wahl ist einfach ByteString. Der mögliche Nachteil von gepinnten Daten ist die Speicherfragmentierung und langsamere Zuweisung, aber das gilt auch für (jede) fremde Zuordnung, die stabile Zeiger zurückgibt.

1

Das Verständnis, wann ein Zeiger referenziert wird, ist nicht trivial. Soweit ich weiß, gibt es keine Möglichkeit zu erfüllen, was Sie fordern, nämlich informieren Sie den GC, dass ein Zeiger nicht mehr erreichbar ist. Im besten Fall kann man einen GC-Zyklus auslösen, aber es gibt keine festen Garantien.

Von Ihrer Beschreibung würden Sie wahrscheinlich lieber einen Verweis-Zählmechanismus anstelle von Garbage Collection bevorzugen. Insbesondere bei komplexem reinem Code ist es jedoch schwierig, die Punkte zu identifizieren, an denen der Zähler inkrementiert oder dekrementiert werden sollte. In einem Zustand oder einer IO-basierten Monade, in der solche Nebenwirkungen richtig sequenziert werden, wäre w.r.t. der Rest der Berechnung.

Wenn Sie nicht wirklich Referenzzählung über "Eins" hinaus benötigen, verwendet eine irgendwie übliche Redewendung eine with-Style-Funktion für die Behandlung der Zuordnung und Freigabe. Das kann ein bisschen schwierig zu handhaben sein.

Zum Beispiel eine triviale Implementierung

-- very simplified code 
withMyResource :: (ResourcePtr -> IO r) -> IO r 
withMyResource action = do 
    p <- allocResourcePtr 
    result <- action p 
    deallocResourcePtr p 
    return result 

Dies kann dann dazu verwendet werden könnte, wie

withResource $ \ptr -> do 
    use ptr 

Beachten Sie, dass dies nicht ganz sicher ist, da könnte man den Zeiger zurückgeben, es Live machen nach seiner Aufhebung

ptr <- withResource return 
use ptr -- dangerous! 

Eine ordnungsgemäße Zeiger-Behandlung routi ne sollte funktionieren wie die ST Monade und ihre getaggten STRef s, die so entworfen sind, um zu verhindern, dass Zeiger ihrem beabsichtigten Umfang entgehen (wie oben getan). Dies nutzt Rang-2-Typen aus, ist aber effektiv.

Noch kann man mit der naiven with Routine leben, und seien Sie vorsichtig, Zeiger nicht entkommen zu lassen.

Ein weiteres Problem mit der Sicherheit wird dadurch verursacht, dass action Ausnahmen auslösen kann. (Dies kann unter Verwendung von bracket -ähnlichen Routinen aus der Bibliothek gehandhabt werden).