GC имеет дело с предсказуемым и зарезервированным ресурсом. ВМ имеет полный контроль над ней и полный контроль над тем, какие экземпляры создаются и когда. Ключевыми словами здесь являются «зарезервированный» и «тотальный контроль». Дескрипторы выделяются ОС, а указатели являются ... хорошо указателями на ресурсы, выделенные за пределами управляемого пространства. Из - за этого, ручка и указатели не ограничены для использования внутри управляемого кода. Они могут использоваться - и часто - управляемым и неуправляемым кодом, выполняющимся в одном и том же процессе.
«Сборщик ресурсов» сможет проверить, используется ли указатель / указатель в управляемом пространстве или нет, но он по определению не знает, что происходит за пределами его пространства памяти (и, что еще хуже, могут использоваться некоторые дескрипторы через границы процесса).
Практическим примером является .NET CLR. Можно использовать ароматизированный C ++ для написания кода, который работает как с управляемым, так и с неуправляемым пространством памяти; дескрипторы, указатели и ссылки могут передаваться между управляемым и неуправляемым кодом. Неуправляемый код должен использовать специальные конструкции / типы, чтобы позволить CLR отслеживать ссылки на его управляемые ресурсы. Но это лучшее, что может сделать. Он не может сделать то же самое с дескрипторами и указателями, и из-за этого Resource Collector не будет знать, можно ли освободить определенный дескриптор или указатель.
редактирование: Что касается .NET CLR, я не имел опыта разработки C ++ с платформой .NET. Возможно, существуют специальные механизмы, позволяющие CLR отслеживать ссылки на указатели / указатели между управляемым и неуправляемым кодом. Если это так, то CLR может позаботиться о времени жизни этих ресурсов и освободить их, когда все ссылки на них будут очищены (ну, по крайней мере, в некоторых сценариях это может). В любом случае, лучшие практики диктуют, что ручки (особенно те, указывающие на файлы) и указатели должны быть освобождены, как только они не нужны. Ресурс Коллектор бы не соблюдающих требования с тем, что это еще одна причина, чтобы не иметь.
редактировать 2: На CLR / JVM / VM в целом довольно просто написать некоторый код для освобождения определенного дескриптора, если он используется только внутри управляемого пространства. В .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);
Существует много методов программирования, которые помогают управлять такими ресурсами.
Программисты C ++ часто используют шаблон под названием Resource Acquisition is Initialization , или сокращенно RAII. Этот шаблон гарантирует, что когда объект, который содержит ресурсы, выходит из области видимости, он закрывает ресурсы, к которым он удерживал. Это полезно, когда время жизни объекта соответствует определенной области в программе (например, когда оно соответствует времени, когда в стеке присутствует определенный кадр стека), поэтому это полезно для объектов, на которые указывают локальные переменные (указатель переменные, хранящиеся в стеке), но не очень полезные для объектов, на которые указывают указатели, хранящиеся в куче.
Java, C # и многие другие языки предоставляют способ указать метод, который будет вызываться, когда объект больше не существует и собирается собирать сборщик мусора. Смотрите, например, финализаторы
dispose()
и другие. Идея состоит в том, что программист может реализовать такой метод, чтобы он явно закрывал ресурс, прежде чем объект будет освобожден сборщиком мусора. Однако у этих подходов есть некоторые проблемы, о которых вы можете прочитать в другом месте; например, сборщик мусора может не собирать объект намного позже, чем вы хотели бы.C # и другие языки предоставляют
using
ключевое слово, которое помогает обеспечить закрытие ресурсов после того, как они больше не нужны (поэтому не забудьте закрыть дескриптор файла или другой ресурс). Это часто лучше, чем полагаться на сборщик мусора, чтобы обнаружить, что объект больше не существует. См., Например, /programming//q/75401/781723 . Общий термин здесь - это управляемый ресурс . Это понятие основывается на RAII и финализаторах, улучшая их в некотором смысле.источник
Вся память одинакова, если я спрашиваю 1K, мне все равно, откуда в адресном пространстве берется 1K.
Когда я запрашиваю дескриптор файла, я хочу дескриптор файла, который я хочу открыть. Наличие дескриптора файла, открытого для файла, часто блокирует доступ к файлу другими процессами или машиной.
Поэтому файловые дескрипторы должны быть закрыты, как только они не нужны, в противном случае они блокируют другие обращения к файлу, но память требуется восстанавливать только тогда, когда вы начинаете исчерпывать его.
Выполнение прохода GC является дорогостоящим и выполняется только «при необходимости», невозможно предсказать, когда другому процессу понадобится дескриптор файла, который ваш процесс, возможно, больше не использует, но все еще имеет открытый.
источник
Я полагаю, что причина, по которой этот подход не был подходящим для других ресурсов, заключается именно в том, что большинство других ресурсов предпочитают выпускать как можно скорее, чтобы их можно было использовать повторно.
Обратите внимание, что, конечно, ваш пример теперь может быть представлен с использованием «слабых» файловых дескрипторов с существующими методами GC.
источник
Проверить, что память больше не доступна (и, следовательно, гарантированно больше не будет использоваться), довольно легко. Большинство других типов ресурсов могут обрабатываться более или менее теми же методами (т. Е. Получение ресурсов - это инициализация, RAII и его аналог освобождения при уничтожении пользователя, что связывает его с администрированием памяти). В общем, сделать какое-то освобождение «точно в срок» невозможно (проверьте проблему остановки, вам нужно будет узнать, что какой-то ресурс использовался в последний раз). Да, иногда это можно сделать автоматически, но это гораздо более запутанный случай с памятью. Таким образом, это зависит от вмешательства пользователя по большей части.
источник