본문 바로가기
Java/About Java

[Java] Java Memory Leak

by seaweed_one 2023. 1. 11.
728x90

안녕하세요. 오늘은 자바의 메모리 누수에 대하여 알아보겠습니다.

자바 개발 시에는 메모리를 고려하지 않아도 된다고 생각하실 수 있습니다.

하지만 자칫하면 메모리 누수가 일어날 수도 있는데요.

메모리 누수의 개념과 발생 원인에 대하여 정리해 보았습니다.

 

메모리 누수란?

일반적 의미의 메모리 누수는 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 현상입니다.

할당된 메모리의 사용을 마친 불필요한 메모리를 반환하지 않는 것이 반복된다면 메모리가 낭비되겠죠?

 

Java의 메모리 관리

자바는 개발자가 메모리 할당과 해제를 명시적으로 수행하지 않습니다.

해서 GC의 개념이 존재하는데요.

GC는 Mark and Sweep이라는 알고리즘을 이용해 가비지 컬렉션을 수행합니다.

Mark는 모든 변수를 스캔하며 각각 어떤 객체를 참조하는지 찾아 마킹합니다.

이때 객체에 유효한 참조가 존재한다면 Reachable, 그렇지 않다면 Unreachable입니다.

Sweep은 Unreachable 한 객체들을 힙에서 제거합니다.

 

조금 더 자세히 알아볼까요?

지난 포스팅에서 MajorGC와 MinerGC의 차이점에 대하여 알아보았죠?

MinerGC는 yong영역의 객체를 삭제하고 JVM의 중지 없이 빠르게 동작한다고 말씀드렸습니다.

Young영역에서 제거되지 않고 시간이 지나면 객체는 Old영역으로 이동됩니다.

MajorGC는 Old영역의 객체를 제거하는 역할을 수행합니다.

이 또한 지난 포스팅에서 언급했었지만 MajorGC 수행 시에는 MajorGC 수행 스레드를 제외한 모든 스레드가 중지됩니다.(Stop-the-world)

 

그럼 객체 사용 종료 시 어떤 영역에서 정리되는 것이 좋을까요?

당연하게도 MinerGC 선에서 정리되는 편이 유리합니다.

MajorGC는 될 수 있으면 지양하는 것이 메모리 관리 측면에서 효율적입니다.

하지만 객체를 다른 변수에서 참조하고 있다면 해당 객체는 MinerGC에서 정리되지 못하고 Old 영역으로 이동하게 되겠죠?

해당 영역에 객체가 계속 누적된다면 MajorGC가 빈번하게 발생하게 되며 프로그램 응답속도 및 성능 저하를 불어옵니다.

또한 Old 영역으로 이동된 후에도 객체가 계속 참조되고 있다면 가바지 컬렉션의 대상이 되지 않는 문제가 발생하겠죠.

 

Java Memory Leak

간단하게 말하자면 자바에서 메모리 누수란 더 이상 사용되지 않는 객체들이 GC에 의해 회수되지 않고 계속해서 누적되는 현상을 일컫습니다.

 

이런 현상은 왜 발생하는 것일까요?

가비지 컬렉션을 통해 객체가 소멸되기 위해서는 객체가 참조되는 곳이 존재해서는 안됩니다.

사용을 마친 객체라도 해당 객체에 대한 참조를 해제하지 않으면 가비지 컬렉션의 대상이 되지 않기 때문에 계속해서 메모리가 할당되는 메모리 누수 현상이 발생하게 됩니다.

최악의 경우 OutOfMemory(OOM) 오류로 프로그램이 종료될 수도 있습니다.

그럼 메모리 누수는 어떤 상황에서 일어날까요?

 

빈번역번수 선언

변수를 지역변수로 선언하게 되면 GC의 범위에 속하게 됩니다.

가능하다면 전역변수보다 지역변수로 선언하는 것이 좋습니다.

 

Static 변수가 객체 참조

Static 변수가 객체를 참조 중이라면 해당 객체는 GC의 대상이 되지 않습니다.

Static 변수는 사용 여부와 관계없이 프로그램 종료 시까지 메모리가 반환되지 않습니다.

Static 변수를 잘 사용한다면 여러 이점이 있지만 그렇지 않은 경우에는 불필요한 메모리를 점유하게 됩니다.

 

Stack에서 Heap에 있는 객체 참조

해당 경우에도 객체는 GC의 대상이 되지 않습니다.

 

무의미한 Wapper 객체 생성

예를 들어 Integer, Long과 같은 클래스입니다.

 

무분별한 Immutable 한 객체 사용

Immutable 한 객체는 GC의 스캔 대상에서 제외되며 컨테이너가 사라질 때 같이 삭제됩니다.

이러한 특성 때문에 가비지 컬렉션의 성능을 높일 수 있지만 잘못 사용 시 계속 쌓여 메모리를 점유할 수도 있습니다.

Immutable 객체가 무엇인지 궁금하신 분들이 계실 텐데요.

이름 그대로 불변의 객체 즉 한번 생성하면 변경할 수 없는 객체들을 일컫습니다.

예를 들면 String 객체가 있습니다.

Wrapper class의 객체는 모두 Immutable이기 때문에 사용 시 주의가 필요합니다.

 

컬렉션 클래스의 데이터를 해제하지 않는 경우

List, Map, Set 같은 컬렉션 클래스들에 객체가 담겨있는 경우가 그렇습니다.

 

Map에 Immutable데이터를 해제하지 않는 경우

특히 Map에는 강력한 참조가 존재하는데요. 해서 내부 객체가 사용되지 않을 때도 GC의 대상이 되지 않습니다.

즉, Map을 더 이상 사용하지 않아도 메모리를 점유하고 있기 때문에 데이터 메모리를 해제하는 것이 바람직합니다.

해당 문제 해결을 위해서는 WeakHashMap을 사용할 수 있는데요.

WeakHashMap의 큰 특징은 특정 key 값이 더 이상 사용되지 않는다고 판단되면 해당 Key - Value 쌍을 삭제합니다.

 

부주의한 자료구조 설계

자료구조 설계 시 사용하지 않는 요소들을 제거하지 않는다면 메모리게 남게 될 수 있습니다.

 

캐시

객체의 참조를 캐시에 넣어두고 캐시를 비우지 않는다면 메모리 누수가 일어날 수 있습니다.

해결 방법으로는 위에서 언급했던 WeakHashMap를 사용할 수 있겠죠.

 

더 자세한 내용은 아래 링크를 참고해 주세요.

https://dzone.com/articles/memory-leak-andjava-code

728x90

'Java > About Java' 카테고리의 다른 글

[Java] 객체 - 클래스  (0) 2023.02.06
[Java] 생성자  (2) 2023.01.31
[Java] Primitive Type & Reference Type  (0) 2023.01.09
[Java] JNA? JNI?  (0) 2023.01.04
[Java] Java Memory  (1) 2022.12.28