
1. Dalvik과 ART
안드로이드에서 사용하는 Dalvik과 ART(Android runtime)은 바이트 코드를 네이티브 코드로 변환하는 런타임이다.
Dalvik의 JIT 컴파일 방식은 앱 실행 시마다 바이트코드를 해석하기 때문에 메모리 사용량이 높고 실행 속도가 느리다는 단점이 있고, GC도 모바일 환경에 최적화되어 있지 않아 구글은 이를 개선하기 위해 ART를 도입하게 된다.
ART의 AOT 컴파일 방식은 설치 시에 미리 네이티브 코드로 변환하고 디스크에 저장한 후 실행 시에 저장된 네이티브 코드를 메모리에 로드해 실행함으로써 실행 속도를 개선하고, GC 메커니즘 또한 모바일 환경에 적합하도록 개선되었다.
2. Dalvik GC의 문제점
Android 5.0 이전까지 사용된 Dalvik의 GC 메커니즘은 여러 비효율적인 측면이 있었다.
초기 모델인 만큼, Dalvik은 객체들의 할당/소멸 주기에 따른 별도의 처리 없이 Heap 영역 전체에 대해 GC를 수행하는 단순한 메커니즘을 사용했고, 이로 인해 단기간에 생성 및 소멸되는 객체가 많은 경우에도 전체 영역을 스캔하여 불필요한 오버헤드가 발생했다.
또한 GC 과정에서 메모리 컴팩션을 수행하지 않았기 때문에 메모리 단편화가 발생할 가능성이 높아, 모바일 환경처럼 메모리 리소스가 제한적인 시스템에서 메모리 관리에 심각한 영향을 끼쳤다.
가장 치명적인 문제점은 Dalvik의 GC 작업은 Stop-The-World(STW) 방식으로 실행되었다는 것이다.
STW 방식의 GC는 수행되는 동안 모든 애플리케이션 스레드가 중단되어 UI 지연이 발생하고, 그에 따라 UX에 부정적인 영향을 미치는 문제가 있었다.
3. ART GC의 특징
이를 개선하기 위해 ART의 GC 메커니즘이 고려한 요소는 아래와 같다.
- 전체 Heap 영역 GC로 인한 높은 처리량을 줄이자 - Generational GC 도입
- 높은 레이턴시를 줄이자 - Concurrent GC 도입
🎈 Generational GC
Dalvik GC는 전체 Heap 영역에 대한 GC로 인해 높은 처리량을 필요로 하는데, ART의 GC는 이를 해결하기 위해 Heap 영역을 YG 영역 OG 영역으로 나누었다.
(1) Young Generation
단기간에 생성 및 소멸되는 객체들이 존재하는 영역이다.
Minor GC가 수행되며, 빠른 회수를 위해 Copying collector 알고리즘을 사용하여 객체들을 새 영역으로 복사하는 방식으로 회수한다.
eden 영역, survivor 영역이 존재한다.
Copying Collector 알고리즘
Minor GC 시 복사 작업을 통해 to-Space에서 from-space로 객체 복사 -> 메모리 단편화 해소
(2) Old Generation
장기간 동안 유지된(YG의 survivor 영역에서 일정 횟수 이상 생존한) 객체들이 존재하는 영역이다.
Major GC가 수행되는 영역이며, 이 영역에서는 Mark-Sweep 또는 Mark-Sweep-Compart 알고리즘을 사용하여 객체를 회수한다.
ART의 Major GC
기본적으로는 Dalvik과 동일한 Mark-Sweep 방식으로 동작하지만, 메모리 단편화 문제가 심화되면 추가적으로 Compact 단계를 수행하여 단편화를 해소하게 된다.
✔️ Mark-Sweep 알고리즘
- Mark 단계
- 루트 객체로부터 접근 가능한 모든 객체를 탐색하고, 살아있는 객체에 "마크"를 설정한다.
- 이를 통해 도달 가능한 모든 객체를 찾아낸다.
- Sweep 단계
- Heap 전체를 순회하며 Mark 단계에서 마크되지 않은 객체들을 회수한다.(메모리 해제)
🎈 Concurrent GC
STW 방식은 높은 지연 시간을 가진다는 단점이 있었다.
ART의 GC는 이를 해결하기 위해, 애플리케이션 스레드와 동시에 실행될 수 있도록 여러 GC 스레드를 활용하여 루트 객체로부터 접근 가능한 모든 객체를 병렬적으로 마킹한다.
4. ART의 GC 동작
(1) 객체 할당과 Eden 영역
- 새로 생성되는 객체는 Young generation 내의 Eden 영역에 할당된다.(아직 장기간 유지되는 객체라는 보장이 없기 때문에)
- Minor GC가 실행되기 전까지, 새로 생성된 객체들은 여기서 유지된다.
(2) Minor GC와 객체 이동
- Young generation 내의 Eden 영역이 다 차면 Minor GC가 발생하고, 이 Minor GC에서 생존한 객체들은 Eden 영역에서 Survivor 영역으로 이동한다.
- Coping Collector 알고리즘을 위해 Survivor 영역은 s0과 s1로 나누어져 있다. 이를 통해 Minor GC가 실행될 때마다 살아남은 객체들을 s0/s1을 번갈아가며 이동한다.
- 이 과정을 반복하다, Survivor 영역에서 일정 횟수 이상 생존한 객체들은 승격하여 Old generation으로 이동하게 된다.
이 때 객체들을 Old generation으로 승격시키는 과정에서 실패하면 Old generation에서 Major GC가 발생한다.
(3) Old generation과 Major GC
- Young generation 내의 Survivor 영역에서 장기간 생존한 객체들이 있는 영역으로, 이 곳의 객체들은 Major GC를 통해 회수된다.
정상적인 상황이라면 Young generation에서 회수되어야 할 객체가 잘못된 참조로 인해 Survivor 영역에서 장기간 살아남아 Old generation으로 승격하게 되면, Minor GC에 비해 상대적으로 자주 수행되지 않는 Major GC를 기다리면서 불필요하게 메모리를 차지하게 되고, 이는 곧 메모리 사용량 증가로 이어진다.
우리는 이를 통해 객체의 참조 범위를 최소한으로 유지하여 Minor GC의 대상이 되도록 유도하는 것이 메모리 관리에 중요한 이유를 알 수 있다.
또한 객체를 장기간 유지하려는 의도가 없는데도 Major GC 시에 회수가 불가능한 경우는 메모리 누수가 발생한다고 판단한다.
5. ART의 GC 최적화 기법
🎈 Card Marking
Heap 영역을 작은 단위(카드)로 나누어 객체들의 변경 사항을 추적한다.
🎈 Write Barrier
Card marking 기법과 연계하여 Heap 영역을 일정 크기의 작은 단위(카드)로 나누고, 해당 카드에 Dirty 플래그를 설정하여 이후 GC 시에 변경된 영역만 집중적으로 스캔할 수 있도록 한다.
🎈 Read Barrier
객체를 읽는 과정에서 해당 객체가 GC에 의해 컴팩션되었을 경우, 올바른 메모리 주소를 참조하도록 보정한다.
컴팩션으로 인한 객체 이동이 발생시킬 수 있는 참조 불일치를 방지함으로써 안정적인 GC의 실행을 보장하는 역할을 한다.
Dalvik과 ART의 비교와, 상대적으로 메모리나 CPU 리소스가 적은 모바일 환경에서 성능과 배터리 효율성을 극대화하기 위해 ART가 어떤 방식으로 GC를 수행하고 최적화하는 지 알아보았다.
Dalvik과 ART의 비교는 안드로이드 기술 면접의 단골 질문이며, 이 주제 하나로 꼬리 질문이 적어도 수십 개는 나올 수 있다는 것을 또 한 번 깨달았다.
'개발 > Android' 카테고리의 다른 글
[안드로이드] Vector Drawable 변환 과정과 Bitmap(feat.dp를 사용하면 모든 화면에서 동일하게 보일까?) (0) | 2025.03.06 |
---|---|
[안드로이드] 쉘 스크립트로 이미지를 dpi 폴더에 분류하기(feat.해상도 대응) (0) | 2025.03.04 |
[안드로이드] 화면 회전과 ViewModel (0) | 2025.02.13 |
[안드로이드] Gson에서 Moshi로 마이그레이션하기 (0) | 2024.12.30 |
[안드로이드] RecyclerView는 어떻게 동작할까? (0) | 2024.06.24 |

1. Dalvik과 ART
안드로이드에서 사용하는 Dalvik과 ART(Android runtime)은 바이트 코드를 네이티브 코드로 변환하는 런타임이다.
Dalvik의 JIT 컴파일 방식은 앱 실행 시마다 바이트코드를 해석하기 때문에 메모리 사용량이 높고 실행 속도가 느리다는 단점이 있고, GC도 모바일 환경에 최적화되어 있지 않아 구글은 이를 개선하기 위해 ART를 도입하게 된다.
ART의 AOT 컴파일 방식은 설치 시에 미리 네이티브 코드로 변환하고 디스크에 저장한 후 실행 시에 저장된 네이티브 코드를 메모리에 로드해 실행함으로써 실행 속도를 개선하고, GC 메커니즘 또한 모바일 환경에 적합하도록 개선되었다.
2. Dalvik GC의 문제점
Android 5.0 이전까지 사용된 Dalvik의 GC 메커니즘은 여러 비효율적인 측면이 있었다.
초기 모델인 만큼, Dalvik은 객체들의 할당/소멸 주기에 따른 별도의 처리 없이 Heap 영역 전체에 대해 GC를 수행하는 단순한 메커니즘을 사용했고, 이로 인해 단기간에 생성 및 소멸되는 객체가 많은 경우에도 전체 영역을 스캔하여 불필요한 오버헤드가 발생했다.
또한 GC 과정에서 메모리 컴팩션을 수행하지 않았기 때문에 메모리 단편화가 발생할 가능성이 높아, 모바일 환경처럼 메모리 리소스가 제한적인 시스템에서 메모리 관리에 심각한 영향을 끼쳤다.
가장 치명적인 문제점은 Dalvik의 GC 작업은 Stop-The-World(STW) 방식으로 실행되었다는 것이다.
STW 방식의 GC는 수행되는 동안 모든 애플리케이션 스레드가 중단되어 UI 지연이 발생하고, 그에 따라 UX에 부정적인 영향을 미치는 문제가 있었다.
3. ART GC의 특징
이를 개선하기 위해 ART의 GC 메커니즘이 고려한 요소는 아래와 같다.
- 전체 Heap 영역 GC로 인한 높은 처리량을 줄이자 - Generational GC 도입
- 높은 레이턴시를 줄이자 - Concurrent GC 도입
🎈 Generational GC
Dalvik GC는 전체 Heap 영역에 대한 GC로 인해 높은 처리량을 필요로 하는데, ART의 GC는 이를 해결하기 위해 Heap 영역을 YG 영역 OG 영역으로 나누었다.
(1) Young Generation
단기간에 생성 및 소멸되는 객체들이 존재하는 영역이다.
Minor GC가 수행되며, 빠른 회수를 위해 Copying collector 알고리즘을 사용하여 객체들을 새 영역으로 복사하는 방식으로 회수한다.
eden 영역, survivor 영역이 존재한다.
Copying Collector 알고리즘
Minor GC 시 복사 작업을 통해 to-Space에서 from-space로 객체 복사 -> 메모리 단편화 해소
(2) Old Generation
장기간 동안 유지된(YG의 survivor 영역에서 일정 횟수 이상 생존한) 객체들이 존재하는 영역이다.
Major GC가 수행되는 영역이며, 이 영역에서는 Mark-Sweep 또는 Mark-Sweep-Compart 알고리즘을 사용하여 객체를 회수한다.
ART의 Major GC
기본적으로는 Dalvik과 동일한 Mark-Sweep 방식으로 동작하지만, 메모리 단편화 문제가 심화되면 추가적으로 Compact 단계를 수행하여 단편화를 해소하게 된다.
✔️ Mark-Sweep 알고리즘
- Mark 단계
- 루트 객체로부터 접근 가능한 모든 객체를 탐색하고, 살아있는 객체에 "마크"를 설정한다.
- 이를 통해 도달 가능한 모든 객체를 찾아낸다.
- Sweep 단계
- Heap 전체를 순회하며 Mark 단계에서 마크되지 않은 객체들을 회수한다.(메모리 해제)
🎈 Concurrent GC
STW 방식은 높은 지연 시간을 가진다는 단점이 있었다.
ART의 GC는 이를 해결하기 위해, 애플리케이션 스레드와 동시에 실행될 수 있도록 여러 GC 스레드를 활용하여 루트 객체로부터 접근 가능한 모든 객체를 병렬적으로 마킹한다.
4. ART의 GC 동작
(1) 객체 할당과 Eden 영역
- 새로 생성되는 객체는 Young generation 내의 Eden 영역에 할당된다.(아직 장기간 유지되는 객체라는 보장이 없기 때문에)
- Minor GC가 실행되기 전까지, 새로 생성된 객체들은 여기서 유지된다.
(2) Minor GC와 객체 이동
- Young generation 내의 Eden 영역이 다 차면 Minor GC가 발생하고, 이 Minor GC에서 생존한 객체들은 Eden 영역에서 Survivor 영역으로 이동한다.
- Coping Collector 알고리즘을 위해 Survivor 영역은 s0과 s1로 나누어져 있다. 이를 통해 Minor GC가 실행될 때마다 살아남은 객체들을 s0/s1을 번갈아가며 이동한다.
- 이 과정을 반복하다, Survivor 영역에서 일정 횟수 이상 생존한 객체들은 승격하여 Old generation으로 이동하게 된다.
이 때 객체들을 Old generation으로 승격시키는 과정에서 실패하면 Old generation에서 Major GC가 발생한다.
(3) Old generation과 Major GC
- Young generation 내의 Survivor 영역에서 장기간 생존한 객체들이 있는 영역으로, 이 곳의 객체들은 Major GC를 통해 회수된다.
정상적인 상황이라면 Young generation에서 회수되어야 할 객체가 잘못된 참조로 인해 Survivor 영역에서 장기간 살아남아 Old generation으로 승격하게 되면, Minor GC에 비해 상대적으로 자주 수행되지 않는 Major GC를 기다리면서 불필요하게 메모리를 차지하게 되고, 이는 곧 메모리 사용량 증가로 이어진다.
우리는 이를 통해 객체의 참조 범위를 최소한으로 유지하여 Minor GC의 대상이 되도록 유도하는 것이 메모리 관리에 중요한 이유를 알 수 있다.
또한 객체를 장기간 유지하려는 의도가 없는데도 Major GC 시에 회수가 불가능한 경우는 메모리 누수가 발생한다고 판단한다.
5. ART의 GC 최적화 기법
🎈 Card Marking
Heap 영역을 작은 단위(카드)로 나누어 객체들의 변경 사항을 추적한다.
🎈 Write Barrier
Card marking 기법과 연계하여 Heap 영역을 일정 크기의 작은 단위(카드)로 나누고, 해당 카드에 Dirty 플래그를 설정하여 이후 GC 시에 변경된 영역만 집중적으로 스캔할 수 있도록 한다.
🎈 Read Barrier
객체를 읽는 과정에서 해당 객체가 GC에 의해 컴팩션되었을 경우, 올바른 메모리 주소를 참조하도록 보정한다.
컴팩션으로 인한 객체 이동이 발생시킬 수 있는 참조 불일치를 방지함으로써 안정적인 GC의 실행을 보장하는 역할을 한다.
Dalvik과 ART의 비교와, 상대적으로 메모리나 CPU 리소스가 적은 모바일 환경에서 성능과 배터리 효율성을 극대화하기 위해 ART가 어떤 방식으로 GC를 수행하고 최적화하는 지 알아보았다.
Dalvik과 ART의 비교는 안드로이드 기술 면접의 단골 질문이며, 이 주제 하나로 꼬리 질문이 적어도 수십 개는 나올 수 있다는 것을 또 한 번 깨달았다.
'개발 > Android' 카테고리의 다른 글
[안드로이드] Vector Drawable 변환 과정과 Bitmap(feat.dp를 사용하면 모든 화면에서 동일하게 보일까?) (0) | 2025.03.06 |
---|---|
[안드로이드] 쉘 스크립트로 이미지를 dpi 폴더에 분류하기(feat.해상도 대응) (0) | 2025.03.04 |
[안드로이드] 화면 회전과 ViewModel (0) | 2025.02.13 |
[안드로이드] Gson에서 Moshi로 마이그레이션하기 (0) | 2024.12.30 |
[안드로이드] RecyclerView는 어떻게 동작할까? (0) | 2024.06.24 |