안녕!

컴퓨터의 메모리 용량이 제한되어 있다고 말해도 놀라지 않을 것 같습니다 :) 일반적으로 RAM 스토리지보다 몇 배 더 큰 하드 드라이브도 좋아하는 게임, TV 쇼, 그리고 더. 이를 방지하려면 현재 메모리 상태를 모니터링하고 컴퓨터에서 불필요한 파일을 삭제해야 합니다. Java 프로그래밍이 이 모든 것과 무슨 관련이 있습니까? 모든 것! 결국 Java 머신이 객체를 생성하면 해당 객체에 대한 메모리를 할당합니다.

실제 대규모 프로그램에서는 수만, 수십만 개의 개체가 생성되며 각 개체에는 할당된 자체 메모리가 있습니다. 그러나이 모든 개체가 얼마나 오래 존재한다고 생각하십니까? 프로그램이 실행되는 전체 시간 동안 "라이브"합니까? 당연히 아니지. Java 객체의 모든 장점에도 불구하고 객체는 불멸이 아닙니다. :) 객체에는 자체 수명 주기가 있습니다. 오늘 우리는 코드 작성에서 약간의 휴식을 취하고 이 프로세스를 살펴볼 것입니다 :) 또한 프로그램이 작동하는 방식과 리소스가 관리되는 방식을 이해하는 데 매우 중요합니다. 그렇다면 사물의 생명은 언제부터 시작되는 것일까요? 사람처럼 — 태어날 때부터, 즉 창조부터.


Cat cat = new Cat(); // Here the lifecycle of our Cat object begins!

먼저 JVM(Java Virtual Machine)은 객체를 생성하는 데 필요한 양의 메모리를 할당합니다. 그런 다음 해당 메모리에 대한 참조를 만듭니다. 우리의 경우에는 해당 참조가 호출되므로 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;

   }

}

Lamborghini Diablo 자동차 개체는 메서드의 두 번째 줄에서 활성 상태를 멈춥니다 main(). 그것에 대한 참조는 하나뿐이었고 해당 참조는 와 동일하게 설정되었습니다 null. Lamborghini Diablo에 대한 참조가 남아 있지 않기 때문에 할당된 메모리는 "쓰레기"가 됩니다. 이를 위해 참조를 null로 설정할 필요는 없습니다.


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객체가 이제 가비지입니다. 이때 GC(가비지 수집기)라는 Java의 내장 메커니즘이 작동합니다.

가비지 수집기는 메모리 해제, 즉 메모리에서 불필요한 개체 제거를 담당하는 내부 Java 메커니즘입니다. 여기에서 로봇청소기 사진을 선택한 데는 그만한 이유가 있습니다. 결국, 가비지 컬렉터는 거의 같은 방식으로 작동합니다. 백그라운드에서 프로그램에 대해 "이동"하여 사실상 사용자의 노력 없이 가비지를 수집합니다. 그것의 임무는 프로그램에서 더 이상 사용되지 않는 객체를 제거하는 것입니다.

이렇게 하면 다른 개체를 위해 컴퓨터의 메모리가 해제됩니다. 수업 초반에 일상 생활에서 컴퓨터 상태를 모니터링하고 불필요한 파일을 삭제해야 한다고 말한 것을 기억하십니까? Java 개체의 경우 가비지 수집기가 자동으로 이 작업을 수행합니다. 가비지 수집기는 프로그램이 실행될 때 반복적으로 실행됩니다. 기술적으로는 가능하지만 이를 명시적으로 호출하거나 명령을 내릴 필요가 없습니다. 나중에 그것에 대해 더 이야기하고 그 작업을 더 자세히 분석할 것입니다.

가비지 컬렉터가 개체에 도달하면 개체를 파괴하기 직전에 finalize()개체에 대한 특수 메서드 — —를 호출합니다. 이 메서드는 개체에서 사용하는 다른 리소스를 해제할 수 있습니다. 메서드 는 클래스 finalize()의 일부입니다 Object. 즉, 이전에 만난 equals(), hashCode()및 메서드 외에도 모든 개체에 이 메서드가 있습니다. toString()다른 방법과 다른 점은 - 어떻게 표현해야 할까요 - 매우 변덕스럽습니다.

특히 객체가 소멸되기 전에 항상 호출되는 것은 아닙니다. 프로그래밍은 정확한 노력입니다. 프로그래머는 컴퓨터에게 무언가를 하라고 지시하고 컴퓨터는 그 일을 합니다. 나는 당신이 이미 이 행동에 익숙하다고 생각하기 때문에 처음에는 다음과 같은 생각을 받아들이기가 어려울 수 있습니다: "객체가 파괴되기 전에 클래스 finalize()의 메서드가 Object호출됩니다. 아니면 호출되지 않을 수도 있습니다. 당신의 행운!"

하지만 사실입니다. finalize()Java 시스템 자체에서 사례별로 메서드를 호출할지 여부를 결정합니다 . 예를 들어 다음 코드를 실험으로 실행해 보겠습니다.


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; // This is when the first object becomes available to the garbage collector
       }
   }

   @Override
   protected void finalize() throws Throwable {
       System.out.println("Cat object destroyed!");
   }
}

객체를 생성한 Cat다음 다음 코드 줄에서 유일한 참조를 null로 설정합니다. 그리고 우리는 그것을 백만 번 합니다. 메서드를 명시적으로 재정의하여 finalize()문자열을 콘솔에 백만 번 출력하도록 했습니다(개체를 파괴할 때마다 한 번씩 Cat). 하지만! 정확히 말하면 내 컴퓨터에서 37,346회만 실행되었습니다! 즉, 27번 중 한 번만 내 컴퓨터에 설치된 Java 컴퓨터가 메서드를 호출하기로 결정했습니다 finalize().

다른 경우에는 가비지 수집이 그것 없이 발생했습니다. 이 코드를 직접 실행해 보십시오. 아마도 다른 결과를 얻게 될 것입니다. 보시다시피 finalize()신뢰할 수 있는 파트너라고 할 수 없습니다 :) 따라서 미래를 위한 작은 조언: finalize()중요한 리소스를 확보하는 방법에 의존하지 마십시오. JVM이 호출할 수도 있고 호출하지 않을 수도 있습니다. 누가 알아?

개체가 살아있는 동안 성능에 매우 중요한 일부 리소스(예: 열린 데이터베이스 연결)를 보유하고 있는 경우 클래스에서 특수 메서드를 생성하여 해제한 다음 개체가 더 이상 존재하지 않을 때 명시적으로 호출하는 것이 좋습니다. 필요합니다. 그렇게 하면 프로그램의 성능이 저하되지 않는다는 것을 확실히 알 수 있습니다. 처음부터 우리는 메모리로 작업하고 쓰레기를 제거하는 것이 매우 중요하다고 말했고 이것은 사실입니다. 리소스를 부적절하게 처리하고 불필요한 개체를 정리하는 방법을 잘못 이해하면 메모리 누수가 발생할 수 있습니다. 이것은 가장 잘 알려진 프로그래밍 실수 중 하나입니다.

프로그래머가 코드를 잘못 작성하면 매번 새로 생성된 개체에 대해 새 메모리가 할당될 수 있는 반면, 오래되고 불필요한 개체는 가비지 수집기에서 제거하지 못할 수 있습니다. 우리는 로봇 진공청소기로 비유를 했으므로 로봇을 시작하기 전에 집안 곳곳에 양말을 흩뿌리고 유리 꽃병을 부수고 바닥에 레고 빌딩 블록을 남겨둔다면 어떤 일이 일어날지 상상해 보십시오. 물론 로봇은 자신의 일을 하려고 노력하겠지만 어느 시점에 이르면 막힐 것입니다.

로봇 청소기가 제대로 작동하려면 바닥을 양호한 상태로 유지하고 로봇이 처리할 수 없는 모든 것을 치워야 합니다. 동일한 원칙이 Java의 가비지 수집기에 적용됩니다. 청소할 수 없는 프로그램에 많은 개체가 남아 있는 경우(예: 로봇 진공 청소기의 양말 또는 레고 빌딩 블록) 어느 시점에서 메모리가 부족할 것입니다. 정지되는 것은 귀하의 프로그램만이 아니라 컴퓨터에서 실행 중인 다른 모든 프로그램이 영향을 받을 수 있습니다. 그들도 메모리가 충분하지 않을 수 있습니다.

이것을 외울 필요는 없습니다. 작동 원리를 이해하기만 하면 됩니다.