JVM 메모리 누수 방지를 위한 체크사항

2024. 9. 29. 10:39·Programming/ETC

- Java, Kotlin 언어를 사용하면 C/C++ 과 다르게 Garbage Collector 가 자동으로 메모리 힙 영역을 정리해주므로, 메모리 관리에 엄격하지 않아도 됩니다.

하지만 백엔드 서버 개발을 맡게된다면 이에 접근하는 클라이언트와 서버가 N 대 1 관계가 형성되기 때문에 실제 서비스에서 메모리 관련 누수에 주의해야만 할 것입니다.

아래는 Java 계열 언어를 사용시 메모리 누수 방지를 위해 체크해야할 사항들을 모아둔 것입니다.

 

1. Static 메모리 사용을 주의하세요.
    Static 영역에 작성한 변수와 데이터는 프로그램의 처음과 끝까지 사라지지 않습니다.
    이 영역의 사용을 지양하도록 합시다.

2. 해제를 전제로 만들어진 라이브러리를 사용할 때에는 꼭 사용 후 해제하도록 합시다.
    해제가 필요한 대표적인 클래스
    PrintStream
    FileInputStream
    FileOutputStream
    BufferedReader
    InputStreamReader

3. 래핑 클래스 구현시 로직상 메모리 해제 처리를 합시다.
    대표적인 예로, Stack 클래스를 구현할 때,
    내부적으로는 데이터의 배열이 존재하고, push 때에는 데이터를 추가하고,
    pop 때에는 데이터를 반환 및 제거를 해야하는데,
    pop 을 하여 데이터를 반환 후 제거 처리를 하지 않으면, 늘어난 데이터가 줄어들지 않고 남아있습니다.
    (덮어써질 지언정 한번 늘어난 리스트는 두번다시 줄어들지 않음.)
    이는 오작동이 아니라 정상 동작이지만 구현 실수라고 할 수 있습니다.

4. 내부 클래스 사용을 주의합시다.
    클래스 안에 클래스를 만드는 기능은 클래스의 정리에 도움이 되지만 주의해야 합니다.
    예를 들어 클래스 안에 내부 클래스를 만들었고,

    해당 내부 클래스를 다른 클래스에서 참조하여 이것이 메모리상 잔류하면,

    해당 내부 클래스를 품고 있는 부모 클래스의 데이터까지 통째로 메모리에 남게 됩니다.
    만약 내부 클래스를 사용하려면, 해당 클래스를 되도록 외부에서 사용하지 못하게 하고,

    사용한다면 메모리 해제에 주의를 해야합니다.
    그게 아니라 단순히 클래스 위치 정리를 위해서라면 내부 클래스가 아닌 중첩 클래스를 사용하세요.
    중첩 클래스 역시 클래스 안에 선언하는 클래스인데, 다른점으로는, 클래스의 이름만을 빌리는 것입니다.
    kotlin 에서는 클래스 내에 단순히 클래스를 선언하면 이것이 중첩 클래스고,

    내부 클래스를 만들려면 class 앞에 inner 를 붙여야 합니다.
    내부 클래스와 중첩 클래스의 차이는,

        중첩 클래스 :
        class Outer {
            val outerProperty = "Outer property"
            class Nested {
                fun nestedFunction() = "Nested function"
            }
        }

        내부 클래스 :
        class Outer {
            val outerProperty = "Outer property"
            inner class Inner {
                fun innerFunction() = "Inner function, accessing: $outerProperty"
            }
        }

    위와 같이 중첩 클래스는 단순히 이름만을 빌리는 것이므로 외부 클래스의 변수에 접근하지 못하고,
    내부 클래스는 접근이 가능합니다.

5. Map 타입 변수 사용시에는 주의합시다.
    예를 들어보겠습니다.

        HashMap<Integer, String> hashMap = new HashMap<Integer, String>();
        Integer key = new Integer(1);
        hashMap.put(key, "1");
        key = null;
        while (true) {
            System.out.println(hashMap);
            System.gc();
            if (hashMap.size() == 0) {
                break;
            }
        }

    위와 같이 HashMap 을 사용하는 코드를 봅시다.
    Integer 타입의 key 변수가 있고,
    map 의 key 로 key 변수를 입력하였습니다.
    이렇게 한다면 hashMap 에 해당 키를 사용하면 그 값이 "1" 에 접근이 가능하겠죠?
    그런데 바로 다음 줄에 key 변수를 null 로 만들어서 해당 값에 접근 자체를 불가능하게 만들었습니다.
    이렇게 된다면 hashMap 의 "1" 이라는 값에는 어떻게 하든 접근이 불가능해지기에

    메모리상 쓸모없는 데이터가 잔류하게 되는 것이죠.
    이러한 쓸모없는 데이터를 정리하기 위해 존재하는 JVM GC 이지만 정작 이를 인지하지 못합니다.
    위 코드에서 hashMap 의 사이즈가 계속해서 1로 유지되기 때문에, while 문에서 빠져나가지 못하게 될 것입니다.

    이것이 첫번째 문제점입니다.
    map 에 저장된 데이터는 접근이 불가능해져도 정리되지 않습니다.

    위 코드의 경우에는 HashMap 이 아닌 WeakHashMap 을 사용하면 해결이 됩니다.
    WeakHashMap 객체를 사용한다면 GC 의 대상이 된다고 이해합시다.

    다음 문제점으로는,
    위와 같은 경우는 key 를 접근 불가로 만드는 경우에 발생하는 문제이므로 애초에 key 를 not nullable 로 바꾸면 됩니다.

    그보다 더 일반적으로 나타날 수 있는 실수가 존재하는데,
    map 안에 데이터를 저장하고,

    개발자 스스로가 필요 없다고 판단할 수 있는 시점에도 해당 데이터를 remove 하지 않은 경우가 해당됩니다.
     이는 특별한 오동작이 아니라 논리적인 정상 동작이지만 단지 실수할 가능성이 높은 케이스입니다.
     map 안의 데이터는 언제든 접근이 가능하도록 설계되어 있으므로

    map 안에 저장된 모든 데이터는 자동으로 지워지지 않으며,
     만약 개발자가 코드상 더이상 사용되지 않는다고 여기는 데이터가 있다면 map 에서 스스로 제거하도록 처리 합시다.

     마지막으로 주의해야 할 점으로는,
     key 의 경우는 되도록 object 타입 변수가 아닌 기본 타입 변수를 사용할 것이며,
     만약 object 타입을 사용한다면 class 의 equals 와 hashCode 메소드를 꼭 구현하여

    다른 변수와 구분이 가능하도록 하세요.

 

6. C/C++ Native 코드 사용

    본인이 JNI 로 C/C++ 코드를 자바에서 사용시에는 당연히 메모리 처리에 신중해야합니다.

    또한 오픈소스 라이브러리 중 실수가 있는 경우도 있을 수 있습니다.



 - 이상입니다.

저작자표시 비영리 변경금지 (새창열림)

'Programming > ETC' 카테고리의 다른 글

[Java] JNI 정리 및 개발 방식 정리  (2) 2024.10.13
[Java] 자바를 사용한 병렬 프로그래밍 정리와 synchronized, volatile 설명  (0) 2024.10.13
Docker 컨테이너 안에서 Docker 사용하기 (Windows, Linux, MacOS)  (5) 2024.10.12
서버 모니터링 시스템 Docker 로 구성하기(Grafana, Prometheus, Loki, Promtail, Springboot)  (4) 2024.10.06
[Kotlin] List 타입 종류  (0) 2024.09.29
'Programming/ETC' 카테고리의 다른 글
  • [Java] 자바를 사용한 병렬 프로그래밍 정리와 synchronized, volatile 설명
  • Docker 컨테이너 안에서 Docker 사용하기 (Windows, Linux, MacOS)
  • 서버 모니터링 시스템 Docker 로 구성하기(Grafana, Prometheus, Loki, Promtail, Springboot)
  • [Kotlin] List 타입 종류
Railly Linker
Railly Linker
IT 지식 정리 및 공유 블로그
  • Railly Linker
    Railly`s IT 정리노트
    Railly Linker
  • 전체
    오늘
    어제
  • 공지사항

    • 분류 전체보기 (108) N
      • Programming (33)
        • BackEnd (18)
        • FrontEnd (2)
        • DBMS (1)
        • ETC (12)
      • Study (74) N
        • Computer Science (21)
        • Data Science (18) N
        • Computer Vision (16)
        • NLP (15)
        • ETC (4)
      • Error Note (1)
      • ETC (0)
  • 인기 글

  • 최근 글

  • 최근 댓글

  • 태그

    Kotlin
    network_mode: "host"
    단축키
    localhost
    unique
    논리적 삭제
    데이터베이스 제약
    docker 배포
    docker compose
    springboot 배포
    kotlin linkedlist
    MacOS
    list
    jvm 메모리 누수
    kotlin mutablelist
    kotlin arraylist
    지리 정보
  • 링크

    • RaillyLinker Github
  • hELLO· Designed By정상우.v4.10.0
Railly Linker
JVM 메모리 누수 방지를 위한 체크사항
상단으로

티스토리툴바