<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jvm on kastori</title><link>http://blog.kastori.dev/tags/jvm/</link><description>Recent content in Jvm on kastori</description><generator>Hugo -- gohugo.io</generator><language>ko-kr</language><lastBuildDate>Mon, 15 Jun 2026 00:00:00 +0900</lastBuildDate><atom:link href="http://blog.kastori.dev/tags/jvm/index.xml" rel="self" type="application/rss+xml"/><item><title>JVM 메모리 구조와 GC — OOM이 왜 발생하고 어떻게 막는가</title><link>http://blog.kastori.dev/tech/2026-06-15-java-04-jvm-memory/</link><pubDate>Mon, 15 Jun 2026 00:00:00 +0900</pubDate><guid>http://blog.kastori.dev/tech/2026-06-15-java-04-jvm-memory/</guid><description>&lt;h2 id="java-코드가-실행되는-환경"&gt;&lt;a href="#java-%ec%bd%94%eb%93%9c%ea%b0%80-%ec%8b%a4%ed%96%89%eb%90%98%eb%8a%94-%ed%99%98%ea%b2%bd" class="header-anchor"&gt;&lt;/a&gt;Java 코드가 실행되는 환경
&lt;/h2&gt;&lt;p&gt;Java 코드를 작성하면 &lt;code&gt;javac&lt;/code&gt;가 &lt;code&gt;.class&lt;/code&gt; 바이트코드로 컴파일하고, JVM이 그것을 실행한다. &amp;ldquo;Write Once, Run Anywhere&amp;quot;가 가능한 이유가 여기 있다. 운영체제마다 다른 JVM이 바이트코드를 해석하기 때문이다.&lt;/p&gt;
&lt;p&gt;JVM 내부를 간단히 표현하면:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ .class 바이트코드 ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ JVM ]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ ClassLoader : 클래스 파일 로드
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ Execution Engine: 바이트코드 실행 (인터프리터 + JIT)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ├─ Garbage Collector: 불필요한 객체 메모리 회수
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; └─ Runtime Data Areas: 메모리 영역
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="heap-vs-stack--면접-단골-질문"&gt;&lt;a href="#heap-vs-stack--%eb%a9%b4%ec%a0%91-%eb%8b%a8%ea%b3%a8-%ec%a7%88%eb%ac%b8" class="header-anchor"&gt;&lt;/a&gt;Heap vs Stack — 면접 단골 질문
&lt;/h2&gt;&lt;p&gt;JVM 메모리에서 가장 중요한 두 영역이다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;method&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// x → Stack&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// user 참조 → Stack, User 인스턴스 → Heap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// s 참조 → Stack, &amp;#34;hello&amp;#34; 객체 → Heap&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 메서드 종료 → x, user, s 참조는 Stack에서 자동 제거&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Heap의 객체는 GC가 나중에 처리&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;구분&lt;/th&gt;
 &lt;th&gt;Heap&lt;/th&gt;
 &lt;th&gt;Stack&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;저장 내용&lt;/td&gt;
 &lt;td&gt;객체 인스턴스 (&lt;code&gt;new&lt;/code&gt;)&lt;/td&gt;
 &lt;td&gt;지역변수, 메서드 호출 정보&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;공유 여부&lt;/td&gt;
 &lt;td&gt;모든 스레드 공유&lt;/td&gt;
 &lt;td&gt;스레드마다 독립&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;GC 대상&lt;/td&gt;
 &lt;td&gt;✅&lt;/td&gt;
 &lt;td&gt;❌ (자동 제거)&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;오류&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;OutOfMemoryError&lt;/code&gt;&lt;/td&gt;
 &lt;td&gt;&lt;code&gt;StackOverflowError&lt;/code&gt;&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;StackOverflowError&lt;/code&gt;는 주로 무한 재귀 호출 때 발생한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="gc--메모리를-자동으로-정리한다"&gt;&lt;a href="#gc--%eb%a9%94%eb%aa%a8%eb%a6%ac%eb%a5%bc-%ec%9e%90%eb%8f%99%ec%9c%bc%eb%a1%9c-%ec%a0%95%eb%a6%ac%ed%95%9c%eb%8b%a4" class="header-anchor"&gt;&lt;/a&gt;GC — 메모리를 자동으로 정리한다
&lt;/h2&gt;&lt;p&gt;Java가 C/C++과 다른 결정적인 차이 중 하나가 GC다. 개발자가 직접 메모리를 해제하지 않아도 된다.&lt;/p&gt;
&lt;h3 id="약한-세대-가설"&gt;&lt;a href="#%ec%95%bd%ed%95%9c-%ec%84%b8%eb%8c%80-%ea%b0%80%ec%84%a4" class="header-anchor"&gt;&lt;/a&gt;약한 세대 가설
&lt;/h3&gt;&lt;p&gt;GC 설계의 핵심 전제다.&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&amp;ldquo;대부분의 객체는 생성 직후 짧은 시간 내에 죽는다.&amp;rdquo;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;HTTP 요청 하나를 처리할 때 생각해보자. 요청 파라미터, DTO, 응답 객체 등 대부분은 요청이 끝나면 더 이상 필요 없다. 반면 서비스 객체, 캐시 데이터는 오래 살아남는다.&lt;/p&gt;
&lt;p&gt;이를 기반으로 Heap을 &lt;strong&gt;Young / Old&lt;/strong&gt; 영역으로 나눈다.&lt;/p&gt;
&lt;h3 id="gc-동작-과정"&gt;&lt;a href="#gc-%eb%8f%99%ec%9e%91-%ea%b3%bc%ec%a0%95" class="header-anchor"&gt;&lt;/a&gt;GC 동작 과정
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Eden (새 객체 생성)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ Minor GC (Eden 꽉 참)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Survivor 0/1 (살아남은 객체, age +1)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ age가 15 초과
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Old Generation (오래 살아남은 객체)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ Old 영역 꽉 참
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Full GC (전체 정리 — 오래 걸림)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Minor GC는 Young 영역만 청소해서 빠르다. Full GC는 전체를 다 보기 때문에 느리다.&lt;/p&gt;
&lt;h3 id="stop-the-world"&gt;&lt;a href="#stop-the-world" class="header-anchor"&gt;&lt;/a&gt;Stop-the-World
&lt;/h3&gt;&lt;p&gt;GC가 실행되는 동안 &lt;strong&gt;애플리케이션의 모든 스레드가 멈춘다&lt;/strong&gt;. 메모리를 정리하는 중에 객체 참조가 바뀌면 안 되기 때문이다.&lt;/p&gt;
&lt;p&gt;Spring 애플리케이션에서 갑자기 응답이 느려지거나 타임아웃이 발생하는 원인 중 하나가 Full GC의 긴 STW다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="gc-종류--지금은-g1gc가-기본"&gt;&lt;a href="#gc-%ec%a2%85%eb%a5%98--%ec%a7%80%ea%b8%88%ec%9d%80-g1gc%ea%b0%80-%ea%b8%b0%eb%b3%b8" class="header-anchor"&gt;&lt;/a&gt;GC 종류 — 지금은 G1GC가 기본
&lt;/h2&gt;&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;GC&lt;/th&gt;
 &lt;th&gt;특징&lt;/th&gt;
 &lt;th&gt;사용 환경&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;Serial GC&lt;/td&gt;
 &lt;td&gt;단일 스레드&lt;/td&gt;
 &lt;td&gt;소형 앱&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;Parallel GC&lt;/td&gt;
 &lt;td&gt;멀티 스레드, 처리량 우선&lt;/td&gt;
 &lt;td&gt;배치 작업&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;G1 GC&lt;/td&gt;
 &lt;td&gt;Region 기반, 예측 가능한 STW&lt;/td&gt;
 &lt;td&gt;Java 9+ 기본&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;ZGC&lt;/td&gt;
 &lt;td&gt;STW 10ms 미만, 초저지연&lt;/td&gt;
 &lt;td&gt;대용량 Heap&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;G1 GC&lt;/strong&gt;는 Heap을 고정 크기 Region으로 나누고 가비지가 많은 Region부터 수집한다. JVM 옵션으로 STW 목표 시간을 설정할 수 있다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-XX:+UseG1GC
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-XX:MaxGCPauseMillis&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="c1"&gt;# 목표 STW 200ms&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;-Xms2g -Xmx4g &lt;span class="c1"&gt;# 최소/최대 Heap 크기&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="oom-왜-발생하나"&gt;&lt;a href="#oom-%ec%99%9c-%eb%b0%9c%ec%83%9d%ed%95%98%eb%82%98" class="header-anchor"&gt;&lt;/a&gt;OOM, 왜 발생하나
&lt;/h2&gt;&lt;p&gt;OutOfMemoryError가 발생하면 로그에 이유가 나온다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;java.lang.OutOfMemoryError: Java heap space
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → Heap 부족. 객체가 너무 많거나 메모리 누수.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;java.lang.OutOfMemoryError: Metaspace
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 클래스가 너무 많이 로드됨.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;java.lang.OutOfMemoryError: GC overhead limit exceeded
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → GC가 열심히 해도 회수되는 메모리가 너무 적음.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;가장 흔한 메모리 누수 패턴:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 1. static 컬렉션에 계속 추가 — GC가 회수 못함&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 이 cache를 지우는 코드가 없으면 계속 쌓임&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 2. ThreadLocal을 제거하지 않음 (스레드 풀 환경)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;threadLocal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 반드시: threadLocal.remove();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 3. 이벤트 리스너 미해제&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 해제하지 않으면 listener 객체가 계속 참조됨&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h2 id="jit-컴파일러--왜-jvm은-워밍업이-필요한가"&gt;&lt;a href="#jit-%ec%bb%b4%ed%8c%8c%ec%9d%bc%eb%9f%ac--%ec%99%9c-jvm%ec%9d%80-%ec%9b%8c%eb%b0%8d%ec%97%85%ec%9d%b4-%ed%95%84%ec%9a%94%ed%95%9c%ea%b0%80" class="header-anchor"&gt;&lt;/a&gt;JIT 컴파일러 — 왜 JVM은 &amp;ldquo;워밍업&amp;quot;이 필요한가
&lt;/h2&gt;&lt;p&gt;JVM은 처음에는 바이트코드를 인터프리터로 실행하다가, 자주 실행되는 코드(핫스팟)를 감지하면 네이티브 코드로 컴파일해서 캐시한다.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;초기 실행: 인터프리터 (느림)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;워밍업 후: JIT 컴파일된 네이티브 코드 (빠름)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Spring Boot 앱을 배포했을 때 처음 몇 분간 응답이 느린 이유가 여기 있다. GraalVM Native Image는 이 워밍업 문제를 해결하기 위해 빌드 시점에 미리 컴파일한다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="마치며"&gt;&lt;a href="#%eb%a7%88%ec%b9%98%eb%a9%b0" class="header-anchor"&gt;&lt;/a&gt;마치며
&lt;/h2&gt;&lt;p&gt;JVM 메모리 구조를 이해하면 &amp;ldquo;왜 이 에러가 났는지&amp;quot;를 추론할 수 있다. &lt;code&gt;StackOverflowError&lt;/code&gt;면 무한 재귀를 의심하고, &lt;code&gt;OutOfMemoryError: Java heap space&lt;/code&gt;면 메모리 누수나 객체 생성 패턴을 살펴봐야 한다.&lt;/p&gt;
&lt;p&gt;다음 편에서는 자주 쓰는 컬렉션 내부가 어떻게 동작하는지 살펴본다.&lt;/p&gt;</description></item></channel></rss>