Сборщик мусора как средство автоматического контроля памяти
В системах, имеющих “сборщик мусора”, программисту не нужно вручную освобождать занятую им память - этим занимается сборщик мусора. Примеров таких систем можно привести множество: .NET, Java, Visual Basic: Следует отметить, что кроме наличия сборки мусора большинство таких языков характеризуются еще и нетипизированностью - объявляя переменную, программист не ограничивает ее тип и может хранить в ней всё что угодно.
Как это происходит? На самом деле всё достаточно просто - достаточно к каждому объекту “привязать” счетчик ссылок, который увеличивается каждый раз, когда на выделенный блок памяти ссылается какая-то переменная, и уменьшается, когда в эту переменную записывается другое значение, т. е. ссылка на объект теряется. К примеру, мы объявили некоторую переменную и тут же создали объект, присвоив ссылку на него этой переменной. Тут же счетчик ссылок этого объекта становится равным 1, т. к. наша переменная ссылается на объект. Теперь мы вызываем некоторую функцию, в качестве одного из параметров которой передаем нашу переменную. Тут счетчик ссылок следует опять увеличить, и он станет равным 2. Естественно, в момент выхода из вызванной функции он вернется в состояние 1. Если мы объявим вторую переменную и присвоим ей значение первой - счетчик вновь увеличится. Как только счетчик ссылок достигает нуля, объект можно удалять, поскольку адрес объекта нигде, ни в какой переменной или блоке памяти не содержится, таким образом, программа “забыла” ее адрес и всё равно никак не сможет прочитать или изменить объект.
В некоторых системах удаление объектов производится именно в тот момент, когда счетчик ссылок становится нулевым. В некоторых момент освобождения памяти объекта откладывается, пока у процессора не появится свободное время (он не будет ничем особенно занят) или свободной памяти не останется.
Кроме очевидного преимущества, у системы сборки мусора есть свои подводные камни. В этих системах невозможно или сложно реализовать “ручные” механизмы выделения и освобождения памяти - к примеру, если на C++ вы можете с легкостью запросить память у операционной системы огромным куском, а внутри этого большого “куска” реализовать свои механизмы выделения/освобождения памяти, перегрузив операторы new и delete, вы теоретически сможете путем ручного управления памятью ускорить работу своего приложения (если, конечно, сможете придумать алгоритм, более оптимальный, чем стандартные), то в таких языках и средах вы этого сделать не сможете. А стандартные методы освобождения и выделения приходится иногда переписывать - например, если вы хотите хранить данные в файле и использовать отображение файлов на память. Другой подводный камень - система сборки мусора может физически переместить объект (естественно, поскольку адрес объекта изменился, все ссылки на него стали недействительными - их надо исправить, и это дело системы сборки мусора). Таким образом, вы не можете “доверять” адресу переменной (если вообще есть возможность его получить), так как он непостоянен. Кроме того, чтобы у сборщика мусора была возможность перемещать объекты физически, объект должен содержать не просто счетчик ссылок, а список самих ссылок, что увеличивает расходы (как расход памяти, так и ресурс времени) на содержание объектов.
