O GC lida com um recurso previsível e reservado . A VM tem controle total sobre ela e controle total sobre quais instâncias são criadas e quando. As palavras-chave aqui são "reservadas" e "controle total". Os identificadores são alocados pelo sistema operacional e os ponteiros são ... indicadores de recursos alocados fora do espaço gerenciado. Por esse motivo, identificadores e ponteiros não são restritos para serem usados dentro do código gerenciado. Eles podem ser usados - e geralmente são - por código gerenciado e não gerenciado em execução no mesmo processo.
Um "Coletor de Recursos" seria capaz de verificar se um identificador / ponteiro está sendo usado dentro de um espaço gerenciado ou não, mas, por definição, desconhece o que está acontecendo fora do espaço de memória (e, para piorar, alguns identificadores podem ser usados através dos limites do processo).
Um exemplo prático é o .NET CLR. Pode-se usar C ++ com sabor para escrever código que funciona com espaços de memória gerenciados e não gerenciados; identificadores, ponteiros e referências podem ser passados entre código gerenciado e não gerenciado. O código não gerenciado deve usar construções / tipos especiais para permitir que o CLR mantenha o rastreamento das referências feitas aos seus recursos gerenciados. Mas é o melhor que pode fazer. Ele não pode fazer o mesmo com alças e ponteiros e, por isso, o Coletor de Recursos não saberia se está certo liberar uma alça ou ponteiro específico.
editar: Em relação ao .NET CLR, não tenho experiência no desenvolvimento de C ++ com a plataforma .NET. Talvez existam mecanismos especiais que permitam ao CLR acompanhar as referências a identificadores / ponteiros entre código gerenciado e não gerenciado. Se esse for o caso, o CLR poderá cuidar do tempo de vida desses recursos e liberá-los quando todas as referências a eles forem limpas (bem, pelo menos em alguns cenários). De qualquer maneira, as práticas recomendadas determinam que manipuladores (especialmente aqueles que apontam para arquivos) e ponteiros devem ser liberados assim que não forem necessários. Um coletor de recursos não estaria em conformidade com isso, esse é outro motivo para não ter um.
editar 2: É relativamente trivial no CLR / JVM / VMs-em-geral escrever algum código para liberar um identificador específico, se for usado apenas dentro do espaço gerenciado. No .NET seria algo como:
// 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);