JavaRush /Java блог /Java Developer /Жизненный цикл объекта
Автор
John Selawsky
Senior Java-разработчик и преподаватель в LearningTree

Жизненный цикл объекта

Статья из группы Java Developer
Привет! Думаю, ты не сильно удивишься, если тебе сказать, что размер памяти на твоем компьютере ограничен:) Даже жесткий диск, который в разы больше оперативной памяти, можно забить под завязку любимыми играми, сериалами и прочим. Чтобы этого не произошло, нужно следить за текущим состоянием памяти и удалять с компа ненужные файлы. Какое отношение ко всему этому имеет программирование на Java? Прямое! Ведь при создании любого объекта Java-машиной под него выделяется память. В реальной большой программе создаются десятки и сотни тысяч объектов, под каждый из которых в памяти выделяется свой кусочек.Жизненный цикл объекта  - 1Но как ты думаешь, сколько существуют все эти объекты? “Живут” ли они все время, пока работает наша программа? Разумеется, нет. При всех достоинствах Java-объектов, они не бессмертны :) У объектов есть собственный жизненный цикл. Сегодня мы чуть-чуть отдохнем от написания кода и рассмотрим этот процесс :) Тем более что он является очень важным для понимания работы программы и распоряжения ресурсами. Итак, с чего же начинается жизнь объекта? Как и у человека — с его рождения, то есть, создания.

Cat cat = new Cat();//вот сейчас и начался жизненный цикл нашего объекта Cat!
Вначале виртуальная Java-машина выделяет необходимый объем памяти для создания объекта. Потом она создает на него ссылку, в нашем случае — cat, чтобы иметь возможность его отслеживать. После этого происходит инициализация всех переменных, вызов конструктора и вот — наш свежий объект уже живет своей жизнью :) Срок жизни у объектов разный, точных цифр здесь не существует. В любом случае, в течение какого-то времени он живет внутри программы и выполняет свои функции. Если говорить точно, объект является “живым” пока на него есть ссылки. Как только ссылок не остается — объект “умирает”. Например:

public class Car {
  
   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");
       lamborghini = null;

   }

}
В методе main() объект машины Lamborghini Diablo перестает быть живым уже на второй строке. На него была всего одна ссылка, а теперь этой ссылке был присвоен null. Поскольку на Lamborghini Diablo не осталось ссылок, он становится “мусором”. Ссылку при этом не обязательно обнулять:

public class Car {

   String model;

   public Car(String model) {
       this.model = model;
   }

   public static void main(String[] args) {
       Car lamborghini  = new Car("Lamborghini Diablo");

       Car lamborghiniGallardo = new Car("Lamborghini Gallardo");
       lamborghini = lamborghiniGallardo;
   }

}
Здесь мы создали второй объект, после чего взяли ссылку lamborghini и присвоили ей этот новый объект. Теперь на объект Lamborghini Gallardo указывает две ссылки, а на объект Lamborghini Diablo — ни одной. Поэтому объект Diablo становится мусором. И в этот момент в работу вступает встроенный механизм Java под названием сборщик мусора, или по-другому — Garbage Collector, GC.
Жизненный цикл объекта  - 2
Сборщик мусора — внутренний механизм Java, который отвечает за освобождение памяти, то есть удаление из нее ненужных объектов. Мы не зря выбрали для его изображения картинку с роботом-пылесосом. Ведь сборщик мусора работает примерно так же: в фоновом режиме он “ездит” по твоей программе, собирает мусор, и при этом ты с ним практически не взаимодействуешь. Его работа — удалять объекты, которые уже не используются в программе. Таким образом он освобождает в компьютере память для других объектов. Помнишь в начале лекции мы говорили, что в обычной жизни тебе приходится следить за состоянием твоего компьютера и удалять старые файлы? Так вот, в случае с Java-объектами сборщик мусора делает это вместо тебя. Garbage Collector запускается многократно в течение работы твоей программы: его не надо вызывать специально и отдавать команды, хотя технически это возможно. Позднее мы еще поговорим о нем и разберем процесс его работы более детально. В момент, когда сборщик мусора добрался до объекта, перед самым его уничтожением, у объекта вызывается специальный метод — finalize(). Его можно использовать, чтобы освободить какие-то дополнительные ресурсы, которые использовал объект. Метод finalize() принадлежит классу Object. То есть, наравне с equals(), hashCode() и toString(), с которыми ты уже познакомился ранее, он есть у любого объекта. Его отличие от других методов в том, что он... как бы это сказать... весьма своенравен. А именно — перед уничтожением объекта он вызывается далеко не всегда. Программирование — штука точная. Программист говорит компьютеру что-то сделать — компьютер это и делает. Ты, полагаю, уже привык к такому поведению, и тебе поначалу может быть сложно принять идею: “Перед уничтожением объектов вызывается метод finalize() класса Object. Или не вызывается. Как повезет!” Тем не менее, это действительно так. Java-машина сама определяет, вызывать метод finalize() в каждом конкретном случае или нет. Например, давай попробуем ради эксперимента запустить такой код:

public class Cat {

   private String name;

   public Cat(String name) {
       this.name = name;
   }

   public Cat() {
   }

   public static void main(String[] args) throws Throwable {

       for (int i = 0 ; i < 1000000; i++) {

           Cat cat = new Cat();
           cat = null;//вот здесь первый объект становится доступен сборщику мусора
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Объект Cat уничтожен!");
   }
}
Мы создаем объект Cat и уже в следующей строчке кода обнуляем единственную ссылку на него. И так — миллион раз. Мы явно переопределили метод finalize(), и он должен миллион раз вывести строку в консоль, каждый раз перед уничтожением объекта Cat. Но нет! Если быть точным, на моем компьютере он отработал всего 37346 раз! То есть только в 1 случае из 27-ми установленная у меня Java-машина принимала решение вызвать метод finalize() — в остальных случаях сборка мусора проходила без этого. Попробуй запустить этот код у себя: скорее всего, результат будет отличаться. Как видишь,finalize() трудно назвать надежным партнером :) Поэтому небольшой совет на будущее: не стоит полагаться на метод finalize() в случае с освобождением каких-то критически важных ресурсов. Может JVM его вызовет, а может нет. Кто знает? Если твой объект при жизни занимал какие-то суперважные для производительности ресурсы, например, держал открытым соединение с базой данных, лучше создай в своем классе специальный метод для их освобождения и вызови его явно, когда объект уже будет не нужен. Так ты точно будешь знать, что производительность твоей программы не пострадает. В самом начале мы сказали, что работа с памятью и удаление мусора очень важны, и это действительно так. Неподобающая работа с ресурсами и непонимание процесса сборки ненужных объектов могут привести к утечке памяти. Это одна из самых известных ошибок в программировании. Неправильно написанный программистом код может привести к тому, что для вновь созданных объектов каждый раз будет выделяться новая память, при этом старые, ненужные объекты будут недоступны для удаления сборщиком мусора. Раз уж мы привели аналогию с роботом пылесосом, представь, что будет, если перед запуском робота разбросать по дому носки, разбить стеклянную вазу и оставить на полу разобранный конструктор Lego. Робот, конечно, попытается что-то сделать, но в один прекрасный момент он застрянет.
Жизненный цикл объекта  - 3
Для его правильной работы нужно держать пол в нормальном состоянии и убирать оттуда все, с чем не справится пылесос. По такому же принципу работает и сборщик мусора. Если в программе будет оставаться много объектов, которые он не может собрать (как носок или Lego для робота-пылесоса), в один прекрасный момент память закончится. И зависнет не только написанная тобой программа, но и все остальные программы, запущенные в этот момент на компьютере. Для них тоже не будет хватать памяти. Вот так выглядят в Java жизненный цикл объектов и сборщик мусора. Это не нужно заучивать: достаточно просто понять принцип работы. В следующей лекции мы поговорим об этих процессах подробнее, а пока — можешь вернуться к решению задач JavaRush :) Успехов!
Комментарии (196)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
2 апреля 2024
В Си с этим более понятно. Захотел выделить память - пожалуйста, malloc. Но не забудь обязательно потом её освободить при помощи free
Бромгексин Уровень 16
12 марта 2024
насколько же жесток этот сборщик мусора... кем нужно быть чтобы уничтожить миллион новорожденных котят😭😭
{Java_Shark} Уровень 17
2 марта 2024
Короче халявщик этот сборщик мусора))) то убирается, то не убирается)))++
Islam Yunusov Уровень 31
14 сентября 2023
Кто пробовал запустить у себя код как в примере из лекции ( System.out.println("Объект Cat уничтожен!");) Я неожиданно озадачился как можно посчитать количество строк или же вызовов в консоли. Кто знает? P.S. Пробовал создать счётчик, но дилемма именно в том, что finalize() вызывается не всегда
Anatoly Уровень 30
27 августа 2023
окей
hidden #3323867 Уровень 21
17 июля 2023
Все пишут, что finalize() устарел. Как мне правильно реагировать на эту информацию?) Garbage collector всё также работает в автоматическом режиме и это изменение никак не повлияло на процесс разработки, или я пропущу что-то важное, если не копну глубже эту тему?
Rustam Уровень 35 Student
10 июня 2023
Определенно нужно глубже разобрать упомянутые в лекции принципы
Ислам Уровень 33
26 мая 2023
Интересная лекция
Andrey Уровень 20
7 мая 2023
Хорошая статья! Спасибо
Emile Уровень 9
18 января 2023
Еще хорошая статья к данной теме: https://topjava.ru/blog/stack-and-heap-in-java