0️⃣ DartVM을 이해하기 전에 먼저 알아야 할 지식들
DartVM, 즉 Dart Virtual Machine에 대해서 제대로 알기 위해선 약간의 사전지식들이 필요하다. 기본적인 OS의 메모리 관리 개념과, Flutter의 release모드, debug모드를 관리하는 JIT,AOT 엔진에 대해 알아야 한다.
우선 Heap과 Stack
Heap과 Stack에 대해서 알아야한다. 모르면 처음부터 이해가 안 된다. 작년에 CS 공부를 열심히 할 때 보다 이해하기 쉽게 정리해놓은 내용이 있으니 꼭 읽어보자. 특히 4번의 Heap영역이 지나치게 크다면? 이라는 항목은 DartVM을 이해하기 위해선 빼놓을 수 없다. 결국 핵심은 Heap 영역에서 이루어지는 GC이기 때문이다.
[얼레벌레 공부하는 CS] Heap과 Stack
0️⃣ 메모리의 영역에서,메모리에 대해서 먼저 기초학습이 필요하다면 아래 블로그 링크를 통해 복습하고 오자! [얼레벌레 공부하는 CS] RAM0️⃣ 메모리란? 메모리의 사전적 정의는 아래와 같
kkevido.tistory.com
1️⃣ DartVM이란?
Dart는 고급 언어다. 컴퓨터는 Dart로 짠 코드를 읽을 수 없으므로 OS 단에서 기계어로 번역하는 과정을 거쳐야한다. 이건 모든 고급 언어에게 해당하는 공통 사항이다. 일반적으로 DartVM은 Dart 프로그램이 실행될 때 생성되지만 Flutter의 경우엔 작동방식이 살짝 다르니 Flutter의 관점에서 다시 살펴보자.

DartVM은 CPU 위에 '버추얼 머신'이라는 이름으로 만들어진 가상의 컴퓨터로서, Dart 코드는 이 VM 위에서 돌아가는 소프트웨어 엔진이다. 익히 들어본 JVM(Java Virtual Machine)과 같은 원리다. 즉, DartVM의 역할은 Dart 코드를 받아서 메모리를 할당하고 기계어로 번역도 해주면서, GC로서의 역할도 한다. 고로 프로세스의 생명주기에 맞추어 동작하는 것이 핵심이다.
그리고 JIT와 AOT 상황에 따라 DartVM은 조금씩 다르게 동작하는데 개발 모드냐 배포 모드냐에 따라 달라진다는 말이다. 이전 포스팅을 아직도 읽지 않았다면 JIT 모드와 AOT 모드가 구체적으로 어떻게 다른지 알고 다시 마저 공부해보도록 하자!
Flutter는 컴파일 언어? 인터프리터 언어? JIT와 AOT의 원리
0️⃣ Flutter는 컴파일 언어인가요 인터프리터 언어인가요? 어느날 갑자기 동료가 물어보았다! "Flutter는 컴파일 언어인가요, 인터프리터 언어인가요?" 컴파일 언어와 인터프리터 언어에 대해 구
kkevido.tistory.com
2️⃣ JIT (Debug Mode)
디버깅 모드로 앱을 실행할 때, 해당 어플리케이션 안에서 DartVM이 생성된다. 해당 VM 안에는 컴파일러, 디버깅이나 분석을 도와주는 도구도 모두 포함이 되어있다. 때문에 앱 용량이 크고 실행 속도도 느리다.
대신, Hot Reload가 실행되는 원리 또한 DartVM이 수행한다. 예를 들어 내가 UI Widget을 고치거나, 수식을 계산하는 함수를 고치면 VM이 이를 인식하고 교체하는 작업을 하게 된다. 때문에 ctrl+s(cmd+s)만 눌러도 빠르게 수정되어 반영되어있는 것처럼 보이게 되는 것이다.
- 개발자가 코드를 수정하고 저장
- DartVM이 변경된 소스 코드 파일만 읽음
- 기존 메모리에 있던 클래스나 함수 코드만 새것으로 덮어씀. 이때의 상태값(State)는 유지!
- VM이 Flutter에게 Rebuild를 명령함
이 4가지 순서가 DartVM의 JIT엔진일 때 동작하는 원리다. 한 마디로 Hot Reload를 도와주는 아주 핵심적인 친구란 말이다!
3️⃣ AOT (Release Mode)
앱을 release 모드로 build하여 앱스토어에 올린 것을 사용자가 다운로드 받아 실행할 땐 AOT 엔진의 VM이 실행된다. 이때 DartVM은 코드를 기계어로 번역하지 않고 기계어로 번역된 파일만 가져와, 이를 런타임으로 실행한다.
이 개념에 대해서 잘 모르겠다! 하면 Dart에서 const와 final의 차이를 네트워크 관점으로 공부한 이전 게시물을 보고 와보자. 생각보다 이해하기 더 쉽다.
Dart의 final과 const의 차이, 네트워크 관점에서 바라보기
0️⃣ Prefer const with constant constructors Flutter 개발을 하다 보면 IDE에서 정말 지겹게 보는 경고문이 있다. 사실 이제는 자주 안 보지만, 어쩌다 한 번씩 까먹게 되면 파란 밑줄이 생기면서 아래와 같
kkevido.tistory.com
컴파일러 기능은 모두 없는 상태의 AOT 모드의 DartVM은 매우 가볍기 때문에 JIT보다 훨씬 더 빠르게 동작하게 된다. 대신 이때의 VM은 Dart Runtime이라는 이름으로 불리며 GC와 스냅샷 관리에만 집중한다. 그렇다면 컴파일러 기능도 release mode에서는 필요 없는 hot-reload 기능도 뺀 AOT모드의 DartVM은 구체적으로 어떤 역할을 하는지 알아보자.
4️⃣ GC로서의 역할과 Isolate
AOT 모드의 DartVM은 GC, 즉 가비지 컬렉터로서의 역할을 한다. 그렇다면 여기서 잠깐! GC는 무엇인가?
GC (Garbage Collection)
더 이상 다른 객체에 의해 참조 되지 않는 객체를 찾아 삭제하는 과정으로, 할당한 메모리 중 더 이상 활용되지 않는 것이 있으면 프로그램이 이를 찾아 자동으로 해제하는 것이다.
GC는 Heap 영역에 존재하면서, Heap에 쌓인 쓰이지 않는 메모리 (Garbage)를 해제하는 일을 담당한다. 그리고 이 Heap 영역은 모든 스레드가 공유하는 영역으로 객체, 전역 변수등을 함께 사용하는 것이 일반적이다.


Dart는 이 Heap을 공유하는 것이 아니라 각 스레드 마다 하나의 Heap 영역을 갖고 있다. 이를 아이솔레이트, Isolate라고 불리우는 단위의 메모리를 갖는다. 보통 스레드라고 부르는 것을, Dart는 아이솔레이트라고 부르는 것이라고 생각하면 편하다.

Dart의 메모리 관리는 스레드 대신 Isolate가 관리하며 각각의 Isolate는 모두 Stack과 Heap을 하나씩 따로 가지는 것이 핵심이다!
이러한 특성 때문에 서로의 메모리를 전혀 참조할 수 없으며 다른 아이솔레이트에 있는 변수도 절대 직접 수정할 수 없고, 장단점이 분명하다.
| 장점 | 단점 |
| 메모리를 공유하지 않으므로 다른 메모리에 접근할 수 조차 없으므로 Lock이 걸리지 않음. | 데이터를 주고 받을 때 비용이 든다. |
| GC가 따로 작동함. (일반적인 언어는 GC가 작동할 때 스레드를 잠시 멈추지만, Dart는 해당 아이솔레이트만 GC를 작동시키면 된다.) |
메모리를 참조할 때 깊은 복사를 해야한다. |
| 위 2가지 이유로 끊김 없이 UI가 작동함. | 위 단점은 아직 Dart가 개선 중이라고 한다. |
5️⃣ 그럼 Flutter에서 GC는 언제 작동할까?
GC의 기본적인 개념을 우선 Android 기준으로 설명해보겠다. (안드로이드 공부는 하다 말았으므로 사실 완벽하게 알고 있는 건 아니라는 점, 양해 부탁한다.)
안드로이드는 세대별 청소부라는 작동원리를 이용하여 GC를 작동한다.
- New Generation
- 갓 태어난 객체들로 비교적 자주, 빠르게 청소 한다.
- Old Generation
- New 세대에서 살아남은 객체들로 가끔 청소 한다.
이렇게 세대를 나눠놓고 청소를 하며, GC가 작동할 때마다 앱이 멈추는 현상이 발생한다. 최신 안드로이드는 멈추는 시간을 0.002초 정도로 매우 짧아서 인간이 인식하지 못할 정도로 발달했다고 한다. 하지만 Flutter는 조금 다르다.
Flutter도 안드로이드처럼 세대별 청소부 원리를 이용한 GC를 작동시키지만, 앞서 설명한 것처럼 메모리의 Heap 영역이 Isolate별로 격리 되어있다는 장점이 있어 보다 효율적으로 GC를 관리한다.
- UI Isolate의 GC (Idle Time)
- Flutter는 1초에 60번씩 화면을 렌더링
- Wiget과 State와 같은 객체를 만들었다가 렌더링이 새로 될 때마다 GC가 작동함
- 1초에 60번 렌더링을 하므로 예를 들어, 49번째 렌더링과 50번째 렌더링을 하는 데에 10ms만 썼다면 그 사이의 6ms 정도의 시간이 남으므로 해당 idle time 때 GC가 작동함
- 다른 Isolate 간의 GC
- 이미지 압축 또는 대용량 JSON 파싱과 같은 작업을 진행할 땐 대용량의 Garbage 발생
- 각 Heap 영역을 고유하게 보유하고 있으므로 compute 함수를 통해 별도로 작업을 시키고, 메인 UI Isolate는 계속 작업을 함
- 따라서 UI 버벅임이 생길 것 같은 작업은 다른 아이솔레이트로 격리하여 작업하면 UI에는 영향이 없게 됨
이렇게 놓고 보니 Flutter의 메모리 관리 방법이 좀 천재적인 것 같기도...? 쩐다.
5️⃣ 마지막 최종 비교
그럼 우리의 최대 경쟁 언어 Kotlin, Swift, React Native 간의 GC 원리는 한 눈에 비교분석 해보자.
| 구분 | iOS (Swift) | Android (Kotlin) | Flutter (Dart) | React Native (JS) |
| 방식 | ARC (참조 카운팅) |
Tracing GC (세대별) |
Tracing GC (세대별) |
Tracing GC (Mark-Seep) |
| 실행 시점 | 즉시 (카운트 0) |
Heap이 찼을 때 | Isolate 별 Heap이 찼을 때 |
엔진이 판단할 때 (JS GC 엔진과의 브릿지 비용 발생) |
| 멈춤 현상 | 없음 | 있음 (유저가 못 느낌) |
있음 (유저가 못 느낌) |
있을 수 있음 |
| 유의사항 | 순환참조 | 메모리 누수 | - | 최적화 |
대충 뭔 말인지 몰라도 각자의 장단점이 뚜렷하다는 것을 알 수 있다. 그 와중에도 iOS는 역시 독자적인 OS 답다~ 싶다. 다음 게시물로는 Dart의 Isolate를 다룰 수 있는 compute 함수에 대해 포스팅 해보겠다.
❗ 출처
참고 사이트1 : https://medium.com/@gauravswarankar/dart-vm-10b027e6fd76
참고 사이트2 : 킹갓제네럴짱짱맨제미나이
'Dart' 카테고리의 다른 글
| Dart는 Single Thread 언어! 비동기는 어떻게? (2) | 2026.01.08 |
|---|---|
| Dart의 final과 const의 차이, 네트워크 관점에서 바라보기 (0) | 2025.12.15 |