feat. JIT & AOT
ART는 Android 4.4(API 19)에서 처음으로 등장했다. 해당 버전에서는 Dalvik과 함께 선택적으로 사용이 가능했다. 하지만 Android 5.0(API 21) 이상에서는 ART(Android Run Time)가 기본 런타임 환경이다.
두 가지의 가장 큰 차이는 컴파일 방식이다. Dalvik은 JIT(Just-In-Time) 방식을 사용하고 ART는 AOT(Ahead-Of-Time) 방식을 사용한다.
Andorid 7.0부터는 JIT 및 AOT 방식을 조합하여 사용한다. 처음 설치할 때 JIT 방식으로 빠르게 설치한 후 자주 사용하는 앱은 기기 미사용 상태나 충전 중일 때 조금씩 컴파일하여 AOT 방식으로 바꾸도록 한다. 각 방식의 장점을 모두 취하려는 시도이다.
두 방식에 대해서 간단히 말하면 다음과 같다.
JIT
- 앱 실행 시 컴파일
- 설치 시 컴파일을 하지 않기 때문에 AOT보다 설치 속도 빠름
- 실행 시 컴파일을 하기 때문에 AOT에 비해 실행 속도 느림
- 용량 작음
AOT
- 앱 설치 시 컴파일
- 설치 시 컴파일을 완료하기 때문에 JIT에 비해 설치 속도 느림
- 실행 시 컴파일을 하지 않기 때문에 JIT에 비해 실행 속도 빠름
- 용량 큼 (JIT에서 실행 시 컴파일하는 것을 미리 컴파일하여 가지고 있기 때문)
왜 JIT과 AOT에 이러한 차이가 있고 Dalvik은 JIT을 쓰고 ART는 AOT를 주로 쓰는 걸까?
Dalvik은 VM이다. 줄여서 DVM이라고 한다. Java를 사용하면서 JVM을 사용하지 않고 DVM을 쓰는 이유는 라이선스 문제나 메모리 효율성 등의 이유가 있다. 결론적으로는 JVM이든 DVM이든 모두 Java를 사용한 VM이기 때문에 비슷한 특성을 가지고 있다.
Java의 컴파일 과정을 살펴보자.
Java는 컴파일러를 통해서 바이트코드를 생성하는 과정까지는 컴파일러 언어의 특징을 갖는다. 하지만 바이트코드는 기계어(Machine Code)가 아니다. 즉 바이트코드는 특정 플랫폼에 종속된 것이 아니며 VM을 위한 코드일 뿐이다. 따라서 VM은 인터프리터를 이용하여 기계어로 해석하는 과정이 필요하다. 이러한 과정은 인터프리터 언어의 특징이라고 할 수 있다.
호환성을 위한 VM의 특성으로 인해 Java는 실행 속도가 느리다. 이런 단점을 보완하기 위해서 JIT 컴파일러를 도입했다.
JIT 컴파일러의 역할은 프로그램 실행 시 자주 사용되는 바이트코드에 대해서는 미리 기계어로 해석해놓는 것이다. 그러면 다시 해당 바이트코드 사용될 때 재해석을 할 필요가 없어 속도가 향상된다.
초기 DVM에는 JIT이 없었고 Android 2.2 버전인 froyo부터 적용되었다. DVM의 JIT은 Trace 방식의 JIT이다. Trace JIT은 Threshold를 초과하면 바이트코드를 기계어로 해석한다. 즉 어떤 구간(ex. if문, for문)이 특정 횟수 이상 반복되면 컴파일한다.
컴파일은 별도의 컴파일러 스레드에 의해서 진행된다. 컴파일이 완료되면 해석된 기계어를 Translation Cache에 저장한다. 이러한 과정을 도식화하면 다음과 같다.
이러한 방법으로 자주 사용하는 부분에 대해 미리 컴파일하여 기계어로 해석해놓기 때문에 실행 성능을 향상 시키긴 했지만 AOT보다는 느리다. 왜냐하면 앞서 잠시 언급했듯이 AOT는 설치 시점에 이미 컴파일을 완료하여 기계어로 해석을 끝냈기 때문이다. 따라서 실행시에는 해석 과정이 없이 곧바로 기계어로 실행이 된다.
next) 안드로이드 Dalvik & ART (2/2)
ref.)
Dreamy 블로그 : http://www.dreamy.pe.kr/zbxe/CodeClip/164835