ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 아이템7. 다 쓴 객체 참조를 해제하라
    책/이펙티브자바 2021. 6. 23. 22:53

    Java의 경우 GC가 메모리 관리를 해준다.

    그래서 메모리 관리에 더 이상 신경을 쓰지 않아도 된다고 오해할 수 있으나 절대 사실이 아니다.

    // 일반적인 Stack 의 pop 메소드
    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
    }

    특별한 문제는 없어 보일 수 있다. 하지만 '메모리 누수' 의 문제가 있는데 이 프로그램을 오래 실행하면 점차 GC 활동과 메모리 사용량이 늘어나 결국 성능이 저하될 것이다.

    상대적으로 드문 경우긴 하지만 디스크 페이징이나 OutOfMemoryError를 일으킬 수 있다.

     

    여기서 문제점은 다 쓴 참조(obsolete reference)를 여전히 가지고 있다는 점이다.

    프로그램에서 그 객체들을 더 이상 사용하지 않더라도 말이다.

     

    해결방안은 해당 참조를 다 쓰면 null(참조 해제)로 초기화 해주면 된다.

    // null 처리한 pop 메소드
    public Object pop() {
          if (size == 0) {
              throw new EmptyStackException();
          }
          Object result = elements[--size];
          elements[size] == null; // 다 쓴 참조 해제
          return result;
    }

    다 쓴 참조를 null 처리하는데 이에는 다른 이점도 따라온다.

    만약 null 처리한 참조를 실수로 사용하려 하면 프로그램이 즉시 NullPointerException 을 던지며 종료한다.

     

    그렇다면 모든 객체를 null로 해야할까 ?

    모든 객체를 다 쓰자마자 일일이 null 처리하는 경우도 있는데 그럴 필요도 없고 바람직하지도 않다.

    객체 참조를 null 처리하는 일은 예외적인 경우여야 한다.

    다 쓴 참조를 해제하는 가장 좋은 방법은 그 참조를 담은 변수를 유효 범위 밖으로 밀어내는 것이다.

     

    메모리 누수

    Stack 과 같이 자기 메모리를 직접 관리하는 클래스

    Stack의 경우 자기 메모리를 직접 관리하는데 활성 영역과 비활성 영역 중 어느 부분이 사용되고 안되는지 GC의 입장에서는 알 수 가 없기에 메모리 누수가 발생할 수 있다.

    해결방안

    • 프로그래머나 아는 사실이므로 비활성 영역이 되는 순간 null 처리해서 해당 객체를 더 이상 쓰지 않을것임을 GC에 알려줘야한다.

    캐시

    객체 참조를 캐시에 넣고 다 쓴 이후에도 그냥 두는 경우가 있다.

    해결방안

    • 캐시 외부에서 Key를 참조하는 동안만 엔트리가 살아있는 캐시가 필요한 경우는 WeakHashMap 을 사용한다.
    • 엔트리의 유효기간을 정해둔다.
      • 사실상 계산하는것이 어렵다
    • 쓰지 않는 엔트리를 청소한다.
      • ScheduledThreadPoolExecutor와 같은 백그라운드 스레드를 활용하거나 캐시에 새 엔트리를 추가할때 부수 작업으로 수행하는 방법을 이용한다.
      • LinkedHashMap의 경우 removeEldestEntry 메서드를 사용해 후자의 방식으로 처리한다.
    • 더 복잡한 캐시를 만들고 싶을때는 java.lang.ref 패키지를 직접 활용한다.

    콜백

    • 콜백이란 이벤트가 발생하면 특정 메소드를 호출해 알려주는 것입니다.(1개)
    • 리스너는 이벤트가 발생하면 연결된 리스너(핸들러)들에게 이벤트를 전달합니다.(n개)
    • 클라이언트가 콜백을 등록만 하고 해지하지 않는다면 콜백은 쌓이게 될 것이다.
    • 이럴 때 콜백을 약한 참조(weak reference)로 저장하면 GC가 즉시 수거해간다.
    • 예를 들어 WeakHashMap에 키로 저장해두면 된다.

    결론

    • 메모리 누수를 방지하는 방법은 다쓴 객체 참조를 null로 처리하는 것과 지역변수의 범위를 최소화 하는 방법이다.
    • 모든 것을 null로 처리한다고 해서 좋은 것은 아니다. 가장 좋은 방법은 지역 변수의 범위를 최소화 하는 방법이다.
    • 메모리 누수의 주범은 자기 메모리를 직접 관리하는 경우, 캐시, 리스너, 콜백이다.
    • 자기 메모리를 직접 관리하는 클래스라면 프로그래머는 항시 메모리 누수에 주의해야 한다.
    • 메모리 누수는 철저한 코드리뷰, 힙 프로파일링 도구를 통해 디버깅을 해야 발견할 수 있기 때문에 메모리 누수를 철저히 신경써야 한다.
킹수빈닷컴