| Главная » Статьи » Java » Java Ranger Basic |
День 9/21
| Garbage Collector Garbage Collector Уборщик мусора убирает те объекты из Heap, на которые нет ссылок. В спецификации виртуальной машины детально не описано, как именно работает garbage collector. Конкретно в JVM HotSpot есть около 5 разных уборщиков мусора. При старте виртуальной машины можно указать ей при помощи флага, какой из GC вызвать. Если не указывать (как все делают), то JVM выберет какой-то GC по умолчанию. У нас уборщик мусора виден единственным образом – методом System.gc(). Но надо понимать, что по спецификации этот метод не вызывает garbage collector, а только передает рекомендации уборщику мусора, что программист считает, что нужно вызвать уборщик мусора. В каждой конкретной реализации виртуальной машины она по-разному реагирует на System.gc(). В общем случае это выглядит так: менеджер памяти вызывает garbage collector, который приходит и обнаруживает объекты в памяти, на которые никто не ссылается и сообщает менеджеру памяти, что эти куски памяти – свободны. Поскольку на эту область памяти больше ссылок не было, ее можно отдать под новые объекты, которые будут там хранить свои данные, и можно быть уверенным, что снаружи никто эту память не поменяет. Бывает 2 основных типа уборщика мусора: Подсчет ссылок. Не используется в Java. Может использоваться в C++. Это выглядит так: сам объект обнаруживает, когда на него появляется новая ссылка. В C++ есть перегрузка операторов, там можно переопределить поведение знака равенства для подсчета того, сколько в данный момент ссылок на объект. При каждом x = new... и y = x счетчик ссылок будет расти. И так же можно перехватывать, когда объект исчезает. Но такая система не может бороться с циклическими ссылками. Поиск достижимых. В Java, C# и большинстве других промышленных языков, где уборщик мусора есть изначально, он сделан по-другому. Когда запускается менеджер памяти, он запускает garbage collector. И он находит все места, откуда можно достичь объект в хипе. Сам по себе объект в хипе ничего сделать не может. Байткоды методов работают в стеке. То есть достичь объект могут либо из стека, либо из PermGen’a. Раньше этот момент назывался stop-the-world, сейчас так никто не делает, это сильно оптимизированно, но можно сказать, что все garbage collectors произошли оттуда. В этот момент замирала вся виртуальная машина, приходил garbage collector, смотрел все стеки, находил все локальные переменные ссылочного типа, смотрел, куда они ссылаются в Heap. Он также приходил в PermGen, находил все статические поля ссылочного типа и смотрел, ссылаются ли они в Heap. Если да, то JVM находил начальный достижимый объект. Далее GC заходил в эти объекты, и смотрел есть ли у них: элементы (если это массивы), поля ссылочного типа (если это объекты). Если есть, то смотрел, куда ссылаются они. И эти объекты JVM обозначал как достижимые. И так она делал до тех пор, пока не оказывалось, что на следующем шаге ни одной новой ссылки не появилось. После этого garbage collector говорит: эти куски памяти трогать нельзя – все остальное свободно. Это очень важный момент – виртуальная машина Java не освобождает память, у нее нет процедуры освобождения памяти. Когда мы вызываем new, то нам нужно подыскать кусок памяти и запустить конструктор. Это приводит к следующему: в Java не важно, как много объектов мы выделяем, важно сколько у нас одновременно живых объектов. Считается, что в хорошей виртуальной машине оператор new работает 10 тактов процессора. В C++ new работает 100 тактов процессора. Более того, в C++ оператор delete тоже работает определенное время. В Java такого оператора нет, тут этим занимается GC в автоматическом режиме. Дело в том, что Java изначально проектировался, как объектно-ориентированный язык, в ней все делается через объекты. И поэтому решили операции выделение и освобождения памяти под объекты оптимизировать насколько возможно. То есть если мы в течении секунды создали миллион простых объектов, то мы потратили на это 10 миллионов тактов процессора. К примеру, на сервере 16-ядерная машина с каждым ядром по 2,5 ГГц, то такой сервер, можно сказать, совершает 40 миллиардов тактов в секунду. Но при таком создании Java может поедать около 1 Гб памяти в секунду. Когда приходит GC и окажется, что в этот момент очень мало объектов живы, он мгновенно обнаруживает, какие объекты трогать нельзя, а значит все остальные свободные. Для Java-программы очень плохо, если одновременно живы очень много объектов. В Java нужно сто раз подумать прежде, чем что-то кэшировать. При этом создание 10 тысяч объектов в секунду – абсолютно нормально для Java. finalize() В хорошо написанном коде метод finalize() должен приходить на 10-100 тысяч строк кода – так утверждает автор метода. Нигде не определен порядок вызова finalize(). Из метода finalize() объект можно оживить. finalize() нужен, если наши объекты включают какую-то часть, которую не может убрать garbage collector. В следующей теме мы рассмотрим: Big O notation Источник: http://becomejavasenior.com/courses/?utm_source=Java+Email+Courses&utm_campaign=aa710df388-JavaRangerBasicIntro&utm_medi | |
| Просмотров: 450 | | |
| Всего комментариев: 0 | |