GC zajmuje się przewidywalnym i zarezerwowanym zasobem. Maszyna wirtualna ma nad nią całkowitą kontrolę i całkowitą kontrolę nad tym, jakie wystąpienia są tworzone i kiedy. Słowa kluczowe są tutaj „zastrzeżone” i „pełna kontrola”. Uchwyty są przydzielane przez system operacyjny, a wskaźniki są ... dobrze wskaźnikami do zasobów przydzielonych poza zarządzanym miejscem. Z tego powodu uchwyty i wskaźniki nie są ograniczone do użycia wewnątrz kodu zarządzanego. Mogą być używane - i często są - przez zarządzany i niezarządzany kod działający w tym samym procesie.
„Kolektor zasobów” byłby w stanie zweryfikować, czy uchwyt / wskaźnik jest używany w zarządzanej przestrzeni, czy nie, ale z definicji nie jest świadomy tego, co dzieje się poza przestrzenią pamięci (i, co gorsza, niektóre uchwyty mogą być używane ponad granicami procesu).
Praktycznym przykładem jest .NET CLR. Można używać aromatyzowanego C ++ do pisania kodu, który działa zarówno z zarządzanymi, jak i niezarządzanymi obszarami pamięci; uchwyty, wskaźniki i referencje mogą być przekazywane między kodem zarządzanym i niezarządzanym. Kod niezarządzany musi używać specjalnych konstrukcji / typów, aby umożliwić CLR śledzenie odniesień do zarządzanych zasobów. Ale to najlepsze, co może zrobić. Nie może zrobić tego samego z uchwytami i wskaźnikami, dlatego wspomniany moduł zbierający zasoby nie będzie wiedział, czy można zwolnić określony uchwyt lub wskaźnik.
edycja: Jeśli chodzi o .NET CLR, nie mam doświadczenia w tworzeniu C ++ na platformie .NET. Być może istnieją specjalne mechanizmy, które pozwalają CLR śledzić odwołania do uchwytów / wskaźników między kodem zarządzanym i niezarządzanym. W takim przypadku CLR może zadbać o żywotność tych zasobów i zwolnić je, gdy wszystkie odniesienia do nich zostaną usunięte (cóż, przynajmniej w niektórych scenariuszach może to zrobić). Tak czy inaczej, najlepsze praktyki określają, że uchwyty (szczególnie te wskazujące na pliki) i wskaźniki powinny zostać zwolnione, gdy tylko nie będą potrzebne. Kolektor zasobów nie przestrzegałby tego, to kolejny powód, aby go nie mieć.
edycja 2: Na CLR / JVM / VMs-ogólnie ogólnie jest dość trywialne napisanie kodu, aby zwolnić określony uchwyt, jeśli jest używany tylko w zarządzanej przestrzeni. W .NET byłoby coś takiego:
// This class offends many best practices, but it would do the job.
public class AutoReleaseFileHandle {
// keeps track of how many instances of this class is in memory
private static int _toBeReleased = 0;
// the threshold when a garbage collection should be forced
private const int MAX_FILES = 100;
public AutoReleaseFileHandle(FileStream fileStream) {
// Force garbage collection if max files are reached.
if (_toBeReleased >= MAX_FILES) {
GC.Collect();
}
// increment counter
Interlocked.Increment(ref _toBeReleased);
FileStream = fileStream;
}
public FileStream { get; private set; }
private void ReleaseFileStream(FileStream fs) {
// decrement counter
Interlocked.Decrement(ref _toBeReleased);
FileStream.Close();
FileStream.Dispose();
FileStream = null;
}
// Close and Dispose the Stream when this class is collected by the GC.
~AutoReleaseFileHandle() {
ReleaseFileStream(FileStream);
}
// because it's .NET this class should also implement IDisposable
// to allow the user to dispose the resources imperatively if s/he wants
// to.
private bool _disposed = false;
public void Dispose() {
if (_disposed) {
return;
}
_disposed = true;
// tells GC to not call the finalizer for this instance.
GC.SupressFinalizer(this);
ReleaseFileStream(FileStream);
}
}
// use it
// for it to work, fs.Dispose() should not be called directly,
var fs = File.Open("path/to/file");
var autoRelease = new AutoReleaseFileHandle(fs);