이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Stack Heap Native Memory에서 붙잡아야 할 핵심 판단 기준은 무엇인가?
  • JVM memory와 OS memory를 분리해서 본다라는 기준은 어떤 JVM/OS 신호로 확인되는가?
  • Java/Spring 코드에서 Stack Heap Native Memory 판단 기준은 어떤 장애 신호로 드러나는가?

개요

이 문서의 주제는 Stack Heap Native Memory이다. Java stack, heap, metaspace, direct buffer, native memory의 경계를 정리한다.

먼저 붙잡을 기준은 다음 문장이다. JVM memory와 OS memory를 분리해서 본다. 이 문서는 이 기준을 Java/Spring 실행 흐름, JVM/OS 관찰 지표, 장애 판단 순서로 연결한다.

핵심 모델

  • 판단 기준: Java memory는 heap만이 아니다.
  • Stack은 thread마다 생기며 thread 수가 늘면 stack memory도 함께 늘어난다.
  • Heap은 객체가 사는 공간이고 GC가 주로 관리하지만, 계속 참조되는 객체는 회수되지 않는다.
  • Native memory에는 direct buffer, metaspace, code cache, native library allocation이 포함된다.
  • RSS는 heap 설정값이 아니라 process가 실제로 물리 memory를 얼마나 쓰는지 보여 준다.

문서별 핵심 구분

  • stack은 thread별 호출 프레임이고 heap은 Java 객체 영역이다.
  • native memory에는 direct buffer, metaspace, thread stack 등이 포함된다.
  • RSS는 heap보다 넓은 process memory 관점이다.

원리

Stack은 thread마다 따로 생기는 호출 프레임 영역이다. thread 수가 늘면 Java heap이 늘지 않아도 thread stack과 native memory 사용량이 늘 수 있다.

Heap은 Java 객체가 저장되는 공유 영역이다. GC가 회수할 수 있는 대상은 도달 불가능한 heap 객체이며, 계속 참조되는 cache, static collection, ThreadLocal 값은 살아 있는 객체로 남는다.

Native memory에는 metaspace, direct buffer, code cache, thread stack, JNI/native library 사용량이 포함된다. OS가 보는 RSS는 이들을 포함한 process 관점이므로 heap 사용량과 같지 않다.

Stack Heap Native Memory 관점에서 중요한 점은 heap after GC와 process RSS를 분리해서 보는 것이다. heap은 안정적인데 RSS만 오르면 direct buffer, native allocation, thread stack, container limit을 의심해야 한다.

Java/Spring 연결

  • Tomcat worker와 executor thread 증가는 stack memory 증가로 이어진다.
  • Multipart upload, WebClient buffer, direct buffer는 heap 밖 압박으로 나타날 수 있다.
  • Spring singleton cache와 static map은 heap baseline을 계속 올리는 출발점이 될 수 있다.
  • -Xmx만 보고 container memory를 잡으면 native 영역과 page cache 여유를 놓친다.
  • Actuator heap/non-heap 지표와 OS RSS를 같이 봐야 JVM 안팎을 구분할 수 있다.

코드와 설정 예시

List<byte[]> retained = new ArrayList<>();
 
public void cache(byte[] payload) {
    retained.add(payload); // 종료 조건 없는 보관은 누수 후보가 된다.
}

예시는 heap에 객체를 계속 붙잡는 가장 단순한 형태를 보여 준다. 실제 서비스에서는 cache, static collection, listener, ThreadLocal, 대량 조회 결과 보관이 같은 패턴을 만들 수 있다.

관찰 명령

jcmd <pid> VM.native_memory summary
jcmd <pid> GC.heap_info
cat /proc/<pid>/status | grep -E 'VmRSS|VmSize|Threads'

Stack Heap Native Memory 관찰 명령은 결론이 아니라 가설 확인 도구다. 운영 중에는 반복 간격, 대상 PID, 수집 시각을 함께 남기고 출력이 큰 명령은 범위를 제한한다.

지표 해석

신호먼저 의심할 것다음 확인
GC 후 heap used가 계속 상승heap leak 또는 cache 증가class histogram, heap dump
heap은 안정적인데 RSS 상승native/direct memory, thread stackNMT, /proc/<pid>/status
Full GC와 pause 증가allocation pressure 또는 old gen 압박GC log, allocation hot spot
Swap in/out 발생OS memory pressurevmstat, container limit, OOM kill log

Stack Heap Native Memory에서는 핵심 신호와 보조 지표를 같은 시간축에서 맞춘다. 먼저 변한 신호와 뒤따라 포화된 신호를 분리해야 원인과 결과를 구분할 수 있다.

장애 상황에서 보는 순서

  1. heap used, non-heap, RSS, thread count를 같은 시각에 남긴다.
  2. OOM 메시지가 heap, metaspace, direct buffer, OS kill 중 무엇인지 확인한다.
  3. thread 증가가 stack memory를 키웠는지 본다.
  4. direct buffer, mmap, native library 사용 변경을 확인한다.
  5. heap dump 전에 class histogram과 GC 후 baseline을 먼저 비교한다.

이 순서는 heap 문제와 native/process memory 문제를 분리하기 위한 루틴이다.

실전 팁

  • heap used after GC와 RSS를 같은 그래프에서 본다.
  • thread 수가 늘면 heap이 아니라 stack/native memory도 같이 확인한다.
  • direct buffer나 Netty memory를 쓰는 서비스는 NMT와 process RSS를 같이 본다.
  • heap dump 전에는 class histogram으로 큰 객체 후보를 먼저 좁힌다.
  • memory 수집 파일에는 민감 정보가 들어갈 수 있으므로 보관 위치와 권한을 먼저 정한다.

주니어 팁

  • Stack Heap Native Memory 주제는 용어 암기보다 정상 상태와 장애 상태에서 어떤 증거가 달라지는지 연결해 익힌다.
  • 처음에는 Stack Heap Native Memory를 재현 가능한 기본 명령과 Actuator 지표로 확인하는 습관부터 만든다.
  • Stack Heap Native Memory 숫자는 크기 자체보다 평소 기준선에서 얼마나 벗어났는지로 판단한다.

시니어 팁

  • Stack Heap Native Memory 주제의 영향이 pool, queue, retry, timeout으로 번지는 경로를 설계 리뷰에 포함한다.
  • Stack Heap Native Memory runbook에는 실행 명령, 정상 범위, 해석 기준, 금지할 대응을 같이 둔다.
  • Stack Heap Native Memory 판단이 container, LB, 다중 인스턴스 환경에서도 같은 의미인지 확인한다.

Guru급 팁

  • Stack Heap Native Memory 주제는 단일 snapshot보다 변화 방향이 중요하다. 같은 명령을 간격을 두고 실행해 상태가 증가, 유지, 회복 중 어디인지 본다.
  • 고급 도구는 Stack Heap Native Memory 가설을 좁힐 때만 사용하고 기본 지표와 모순되는지 먼저 확인한다.
  • Stack Heap Native Memory 조치 후에는 CPU, memory, socket, disk, pool 중 다음 포화 지점이 어디로 이동했는지 확인한다.

위험 신호!

  • jvm.memory.used만 보고 OS OOM kill을 설명하려 한다.
  • thread 수 증가를 memory 증가 원인에서 제외한다.
  • heap은 안정적인데 RSS가 오르는 상황을 GC 문제로만 본다.
  • direct buffer와 metaspace 한계를 모른 채 heap size만 키운다.
  • heap dump를 아무 경로에 남겨 disk full이나 정보 노출을 만든다.

확인 질문

확인 질문

  • Stack Heap Native Memory 주제를 장애 중 확인할 때 Application 로그만으로 부족한 이유는 무엇인가?
    • 로그는 코드 경로를 보여 주지만 Stack Heap Native Memory의 JVM/OS 자원 상태, 대기 위치, 포화 방향은 별도 지표로 확인해야 하기 때문이다.
  • Stack Heap Native Memory 관련 설정이나 pool 크기를 바꾸기 전에 확인할 것은 무엇인가?
    • 먼저 변한 신호, 하위 자원 capacity, 변경이 완화인지 증폭인지 판단할 기준을 Stack Heap Native Memory 지표로 확인해야 한다.
  • Stack Heap Native Memory 재발을 막기 위해 장애 기록에 무엇을 남겨야 하는가?
    • 발생 시간, 영향 범위, 사용한 명령, Stack Heap Native Memory 핵심 지표, 원인 판단, 완화 조치, 근본 수정, 남은 리스크를 남긴다.

참고 문서