GC处理可预测和保留的资源。VM对其具有完全控制权,并完全控制创建哪些实例以及何时创建实例。此处的关键字为“保留”和“完全控制”。句柄是由OS分配的,而指针则是指向在托管空间之外分配的资源的指针。因此,句柄和指针不限于在托管代码中使用。它们可以(并且经常)由在同一进程上运行的托管和非托管代码使用。
“资源收集器”将能够验证是否在托管空间内使用了句柄/指针,但是根据定义,它不知道其内存空间之外发生了什么(而且,更糟糕的是,可以使用某些句柄)跨过程边界)。
一个实际的例子是.NET CLR。可以使用调味的C ++编写可同时用于托管和非托管内存空间的代码。句柄,指针和引用可以在托管和非托管代码之间传递。非托管代码必须使用特殊的构造/类型,以允许CLR跟踪对其托管资源的引用。但这是最好的。它不能对句柄和指针做同样的事情,因此,资源收集器将不知道是否可以释放特定的句柄或指针。
编辑:关于.NET CLR,我对使用.NET平台进行C ++开发没有经验。也许有一些特殊的机制可以使CLR跟踪对托管和非托管代码之间的句柄/指针的引用。如果是这种情况,那么CLR可以照顾这些资源的生命周期,并在所有对它们的引用都被清除时释放它们(嗯,至少在某些情况下可以)。无论哪种方式,最佳实践都要求在不需要它们时应立即释放句柄(尤其是指向文件的句柄)和指针。资源收集器将不遵守该要求,这是另一个原因。
编辑2:在CLR / JVM / VMs-general上,如果仅在托管空间内使用某些代码来释放特定的句柄,则相对来说比较琐碎。在.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);