[Java] Garbage Collection을 사용해도 leak이 왜 발생할까?

윤동환·2023년 2월 3일
0

Java

목록 보기
1/3
post-thumbnail

메모리 누수(Leak)이란

컴퓨터 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 현상이다.
더 이상 불필요한 메모리가 해제되지 않으면서 메모리 할당을 잘못 관리할 때 발생한다.

Java에선 GC(Garbage Collection)이 대부분의 메모리를 관리하여 불필요한 메모리를 해제시켜주지만 몇몇의 경우엔 더이상 사용되지 않는 객체들이 소멸되지 않고 누적되어 Leak이 발생한다.

Garbage Collection이란?

C언어의 free()라는 함수를 대신하여 JVM의 가비지 컬렉터가 불필요한 메모리를 정리해 주는 것

JVM의 영역관리

JVM의 Heap영역은 2가지를 전제로 설계되었다.
1. 대부분의 객체는 금방 접근 불가능한 상태가 된다.
2. 오래된 객체에서 새로운 객체로의 참조는 거의 일어나지 않는다.

Young 영역

  • 새롭게 객체가 할당되는 영역
  • Young 영역에 대한 가비지 컬렉션(Garbage Collection)을 Minor GC라고 부른다.

Miner GC

Young 영역에 오래되지 않은 객체를 사용이 끝나고 해제시켜준다.
소요시간(대체로 1초 미만)은 Young 영역의 크기에 따라 다르다. JVM을 중지하지 않으며 빠르고 효율적으로 동작한다.

Old 영역

  • Yonng 영역에서 Reachable 상태를 유지하며 살아남은 객체가 복사되는 영역
  • Yonng 영역보다 크게 할당되며 영역의 크기가 큰만큼 가비지는 적게 발생한다.
  • Old 영역에 대한 가비지 컬렉션(Garbage Collection)을 Major GC 또는 Full GC라고 부른다.

    old영역이 크게 할당되는 이유는 큰 객체들은 바로 Old 영역에 할당되기 때문이다.
    이러한 상황때문에 Old객체가 Young 객체를 참조하는 경우도 존재한다. 이를 대비하기 위하여 Old 영역에 512 bytes로 되어있는 Card Table이 존재한다.

Major GC

Old 영역의 객체를 소멸시키는 역할을 수행한다.

Miner GC에서 정리되지 않은 객체가 오래되면 Young 영역에서 Old 영역으로 이동시킨다.

Card Table

  • Old 영역의 객체가 Young 영역의 객체를 참조할 때마다 그에 대한 정보를 표시함
    -> Young영역에서 minor GC가 실행될 때 모든 Old 영역에 존재하는 객체를 검사하지 않고 Card Table만 조회하여 GC의 대상인지 식별하기 위함

GC의 동작 방식

  1. Srop-the-world
    모든 Thread를 멈추는 Srop-the-world를 실행하여 참조되지 않는 객체를 Heap에서 소멸시킨다. GC의 성능개선을 하기위해 Thread 중지 시간을 줄이는 작업을 한다.

  2. Mark & Sweep

  • Mark : 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업

    Stop The World를 통해 모든 작업을 중단시키면, GC는 스택의 모든 변수 또는 Reachable 객체를 스캔하면서 각각이 어떤 객체를 참고하고 있는지를 탐색하여 마킹함

  • Sweep : Mark 단계에서 사용되지 않음으로 식별된 메모리 해제

    Mark가 되지 않은 객체들을 메모리에서 제거함

Leak이 발생하는 패턴들

  1. 무의미한 Wapper 객체를 생성하는 경우
    GC는 Immutable 객체를 skip하며 컨테이너 자체가 사라질 때 같이 삭제한다.
    StringBuilder와 달리 String과 같은 Immutable객체는 계속 힘에 쌓여 메모리를 점유하는 단점이 있으므로 무분별하게 사용하면 불필요한 메모리를 점유할 수 있다.

  2. Map에 Immutable 데이터를 해제하지 않은 경우
    Map에는 강력한 참조가 있어서 내부 객체가 사용되지 않더라고 GC대상이 되지 않는다. Map 내부에 사용하지 않는 데이터는 메모리를 해제하는 것이 좋다.

  3. 부실한 Try Catch 설계

    try {
       Connection con = DriverManager.getConnection();
       // 예외 발생 !!!
       con.close();
    } Catch(exception e) {
    }

    close하기 전 예외가 발생할 수 있으므로 connection을 close해주는 부분을 catch에서 할 수 있도록 해주거나 별도의 조치를 취해야 한다.

  4. 더이상 참조되지 않는 곳의 데이터를 갖고있을 때
    stack을 구현했다 가정 시 pop()을 할 때, 기존에 참조하던 메모리의 데이터를 null로 초기화 혹은 삭제 해주지 않는다면 댕글링 포인터가 생길 수 있다.

GC 튜닝

Java 실행시 GC의 방식, 메모리 크기를 지정할 수 있다.

//힙 영역의 구성을 정의해서 App을 Run하는 예제
java -Xms=256m -Xmx=1536m -XX:NewSize=32m -XX:MaxNewSize=512m -XX:NewRatio=2 App

Options

  • Xms, -Xmx [ Heap의 최소, 최대 Size ]
  • XX:NewSize [ Young 영역의 초기 Size ]
  • XX:MaxNewSize [ Young 영역의 최대 Size ]
  • XX:NewRadio [ Young 영역 대비 Old 영역 Size의 비율 ]

참고 블로그
참고 블로그2

profile
모르면 공부하고 알게되면 공유하는 개발자

0개의 댓글