일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- android
- MiTweet
- Coroutine
- ProGuard
- AWS
- Gradle
- Python
- Compose
- Codeforces
- 백준
- relay
- TEST
- 쿠링
- 코루틴
- activity
- textfield
- GitHub
- Coroutines
- MyVoca
- 코드포스
- Hilt
- boj
- androidStudio
- livedata
- architecture
- Kotlin
- Rxjava
- 프로그래머스
- pandas
- 암호학
- Today
- Total
이동식 저장소
Android Runtime with ART, AOT, JIT, DEX 본문
제목에 있는 네 가지 단어를 모두 설명할 수 있다면 이 글을 읽을 필요가 없다. 즉시 다른 공부를 하도록 하자. ㅋㅋ
Android
Android는 리눅스를 기반으로 개발된 오픈 소스 소프트웨어 스택이다. 진짜 스택처럼 생겼다.
최하단의 리눅스 커널을 기반으로 하여, 하드웨어를 추상화하고, Android 런타임과 핵심 라이브러리를 쌓아서, Java API 위에서 애플리케이션이 실행된다. 이것이 Android의 핵심 구조이다.
가운데의 Android Runtime은 하드웨어 레이어 위에서 동작하며, Native C/C++ 라이브러리를 Java API로 제공한다. 예를 들어 Android Framework가 제공하는 Java OpenGL API를 사용하여 2D/3D 그래픽을 직접 그릴 수 있다.
Android는 Java로 작성된 API 세트와 같다. Activity, Resource 등의 Java API를 사용하여 앱을 개발하는 것이다.
Java와 Kotlin 중 뭘 쓰던 간에 결국 컴파일하면 JVM 바이트코드가 나온다. 알다시피 JVM 바이트코드를 실행하려면 JVM이 필요하다.
그런데 JVM은 어디 있죠?
눈을 씻고 찾아봐도 위의 사진엔 JVM이 없다. 분명 Android Runtime 근처에 있어야 할 텐데..?
사실 Android는 JVM 바이트코드를 직접 실행하지 않는다. Java가 구글 것이 아니기 때문이다. 대신 안드로이드 런타임이 자체적인 바이트코드를 실행한다.
위의 그림처럼 컴파일된 JVM 바이트코드(.class 파일)가 d8/r8 컴파일러를 통해 dex(Dalvik Executable)로 컴파일된다. R8 컴파일러가 무엇인지 궁금하다면 이 글을 읽어보자.
Dalvik?
Dex 파일도 결국 바이트코드라, 실제로 앱을 실행할 땐 dalvik 런타임이 기계어로 변환하여 실행했다.
초창기 dalvik은 앱이 실행될 때 실시간으로 코드를 변역하였다. Python과 같은 인터프리팅 방식인데, 알다시피 인터프리팅 언어는 무지하게 느리다!
그래서 dalvik에 JIT(Just-In TIme) 컴파일러가 추가되었다. JIT 방식을 사용하면 앱이 실행될 때 코드의 일정 부분이 한꺼번에 변역되어 메모리에 저장된다. 이렇게 하면 적어도 번역된 부분에서는 성능이 좋아진다.
ART의 등장
Android 5.0에서 dalvik 런타임이 ART(Android Runtime)로 교체된다. 각 앱은 자신의 프로세스 안에서 자체적인 ART 인스턴스로 실행된다. 즉 모든 앱은 독립적인 ART 위에서 실행된다.
동시에 AOT(Ahead-On Time) 컴파일러가 추가된다. 앱이 점점 커지면서 JIT가 너무 많이 실행되는 문제점이 생겼는데, AOT는 앱이 실행되기 전에 모든 코드를 기계어로 번역하여 저장해 놓고, 번역된 기계어를 실행한다. 바이트코드가 아닌 기계어가 실행되기 때문에 당연히 성능이 크게 향상되었다.
ART는 dex 바이트코드를 oat 파일(기계어)로 변환하여 저장한다. 주로 앱을 설치할 때 번역한다.
물론 AOT에도 단점이 있다. 앱이 클 수록 번역하는 시간도 오래 걸리는데, 주로 앱을 설치할 때 번역하므로 설치 시간이 길어지는 문제가 있다. 바이트코드를 풀어서 기계어로 쓰기 때문에 앱의 용량도 늘어난다.
혼용
그래서 Android 7.0(Nougat)부터는 AOT와 JIT를 모두 활용한다. 정확히는 앱을 처음 실행할 때는 무조건 JIT로 실행하여 설치 시간을 줄이고, 앱을 사용하지 않을 때 AOT로 조금씩 컴파일하는 것이다. 이렇게 하면 설치 시간과 성능 모두 잡을 수 있다.
이런 이유로 맨 위의 안드로이드 런타임이 자체적인 바이트코드를 실행한다는 문장은 반만 맞는 말이다. 처음에는 바이트코드를 기계어로 번역하여 실행하지만, 나중에 AOT 컴파일이 끝나면 바이트코드 대신 번역된 기계어가 실행되기 때문이다.
왜 이렇게 복잡하죠?
내 생각에는 플랫폼에 독립적인 OS를 만들고 싶었던 것 같다. 그래서 똑같이 플랫폼에 독립적인 Java를 채용한 것이다. 물론 JVM을 OS에 탑재할 수는 없지만, 자체적인 런타임을 만들고 JVM 바이트코드를 런타임에 맞게 변환하면 되니까.
뭔가 비효율적인 것 같지만 어쨌든 Android는 성공했다. 세계의 75%를 장악하는 일이 쉬운 줄 아나.
이제 제목에 있는 개념 4개를 알겠지? 헷갈리지 말고 기억해 두자.
참고자료
'Primary > Android' 카테고리의 다른 글
[Android] 코드 경량화 시 Instrumented test가 실행되지 않는 오류 (0) | 2022.07.19 |
---|---|
[Android] Build variant 기초 (0) | 2022.07.17 |
[Android] R8 컴파일러로 앱 경량화하기 (0) | 2022.07.09 |
[Android] proguard-rules.pro는 무엇인가 (0) | 2022.07.07 |
Hilt Test Principles (0) | 2022.06.30 |