- 다 쓴 객체 참조를 해제하라2021년 03월 28일 22시 33분 41초에 업로드 된 글입니다.작성자: jCurve728x90반응형
자바의 gc가 메모리 관리를 해주기 때문에 메모리에 신경을 쓰지 않아도 된다고 생각하지만 그렇지 않다.
위의 코드에서 stack 자료구조를 구성하는 elements 참조 변수가 pop() 함수에서 최상위 데이터를 꺼낸 후 다시 쓸 일이 없는 객체의 참조가 아직 stack에 남아 있는데 이를 방치하고 프로그램을 오래 실행하면 가비지 컬렉션 활동과 메모리 사용량이 늘어 날 것이다. 또한 디스크 페이징이 일어나 OutOfMemoryError를 보게될 수 있는데
디스크 페이징이란 프로그램중 자주 사용되지 않는 부분의 작업 메모리를 주기억장치인 메모리로부터 보조기억장치인 하드디스크로 옮기는 방식을 통해, 활용 가능한 메모리 공간을 증가시키기 위한 기법 중 하나이다. 이때, 한번에 옮겨지는 메모리 용량 단위를 페이지라 부른다.
가비지 컬렉션 언어에서는 (의도치 않게 객체를 살려두는) 메모리 누수를 찾기가 까다로운데, 객체 참조 하나를 살려두면 가비지 컬렉터는 그 객체뿐 아니라 그 객체가 참조하는 모든 객체(재귀적으로 참조하는 객체까지)를 회수하지 못한다. 이러한 부분은 성능에 악영향을 줄 수 있다.
해법은 참조를 다 쓴 후 null 처리(참조 해제)하면 된다.
이번엔 pop() 메소드를 실행할 때 스택에 더 이상 사용하지 않는 객체에 대한 참조를 null로 해제해준다.
다 쓴 참조를 null로 처리하면 얻는 이점이 또 있는데, null 처리한 참조를 실수로 사용하려 하면 NullPointException을
컴파일 오류로 받아볼 수 있다.
모든 객체를 다 쓰자마자 null로 처리할 필요는 없고 바람직하지도 않다.
null처리는 자기 메모리를 직접 관리하는 클래스에 많이 보이는 취약점이며, 위의 예시에서 stack 객체 내부의 elements 배열로 저장소 풀을 생성 후 활성 영역에 속한 원소들은 사용되고 비활성 영역은 쓰이지 않는 객체들은 사용되지 않는다는 점을 gc는 모르고 작성자 본인만 알기 때문에 null로 처리를 해줘야하는 것이다.
캐시 역시 메모리 누수를 일으키는 주범이다.
이에 대한 해법중 하나로 외부에서 key를 참조하는 동안 엔트리(key와 value로 구성되는 데이터)가 살아 있는 캐시가 필요한 상황에서는 WeakHashMap을 사용해 캐시를 만든다.
캐시를 만들 때 보통의 경우 엔트리의 유효 기간을 정확히 정의하기가 어렵기 때문에 시간이 지날수록 엔트리의 가치를 떨어뜨리는 방식을 사용한다.
(ScheduledThreadpoolExecutor같은) 백그라운드 스레드를 활용하거나 캐시에 새 엔트리를 추가할 때 부수 작업으로 수행하는 방법이 있다.
LinkedHashMap은 removeEldestEntry 메서드를 사용해 후자의 방식으로 처리한다.
리스너(Listener)혹은 콜백(Callback)도 메모리 누수의 주범이다.
이들은 클라이언트가 콜백을 등록만 하고 명확히 해지하지 않기 때문에 조치 해주지 않는다면 게속해서 쌓여갈 것이다.
이럴 때는 약한 참조로 저장하면 가비지 컬렉터가 즉시 수거해간다. (WeakHashMap에 키로 저장)
반응형'JAVA' 카테고리의 다른 글
try-finally 보다는 try-with-resource를 사용하라 (0) 2021.03.28 finalizer와 cleaner 사용을 피하라 (0) 2021.03.28 불필요한 객체 생성을 피하자 (0) 2021.03.15 자원을 직접 명시하지 말고 의존 객체 주입을 사용하자 (0) 2021.03.15 인스턴스화를 막으려거든 private 생성자를 사용하자 (0) 2021.03.15 댓글