GC має справу з передбачуваним та зарезервованим ресурсом. ВМ має повний контроль над ним і має повний контроль над тим, які екземпляри створюються та коли. Ключові слова тут "зарезервовані" та "тотальний контроль". Ручки розподіляються ОС, а покажчики - це ... добре вказівники на ресурси, виділені поза керованим простором. Через це, ручки та покажчики не можуть використовуватись всередині керованого коду. Вони можуть використовуватися - і часто є - керованим і некерованим кодом, що працює в одному і тому ж процесі.
"Колектор ресурсів" міг би перевірити, чи використовується ручка / покажчик у керованому просторі чи ні, але він за визначенням не знає, що відбувається поза його простором пам'яті (і, щоб погіршити ситуацію, деякі ручки можуть бути використані через межі процесу).
Практичний приклад - .NET CLR. Можна використовувати ароматизований C ++ для написання коду, який працює як з керованими, так і з некерованими просторами пам'яті; ручки, покажчики та посилання можуть передаватися між керованим та некерованим кодом. Некерований код повинен використовувати спеціальні конструкції / типи, щоб CLR міг відслідковувати посилання на керовані ресурси. Але це найкраще, що можна зробити. Це не може зробити те ж саме з ручками та покажчиками, і через це зазначений Колектор ресурсів не знає, чи нормально випустити певну ручку чи покажчик.
редагувати: Що стосується .NET CLR, я не маю досвіду розробки C ++ з платформою .NET. Можливо, є спеціальні механізми, які дозволяють CLR вести відстеження посилань на ручки / покажчики між керованим та некерованим кодом. Якщо це так, CLR може подбати про життя цих ресурсів і випустити їх, коли там будуть очищені всі посилання на них (ну, принаймні, в деяких сценаріях). Так чи інакше, найкраща практика диктує, що обробляти (особливо ті, що вказують на файли) та покажчики, слід випускати, як тільки вони не потрібні. Колектор ресурсів не дотримується цього, це ще одна причина його не мати.
редагувати 2: В цілому CLR / JVM / VMs відносно тривіально написати якийсь код, щоб звільнити певну ручку, якщо вона використовується лише всередині керованого простору. У .NET буде щось на кшталт:
// 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);