Компиляторам требуется две вещи для генерации кода исполнителей: информация и ресурсы.
Составители JIT имеют больше информации в своем распоряжении, чем компиляторы AOT. Статический анализ невозможен в общем случае (почти все, что вы хотели бы знать о программе, можно свести к проблеме остановки или теореме Райса) и трудно даже в частном случае. У JIT-компиляторов нет этой проблемы: им не нужно статически анализировать программу, они могут наблюдать ее динамически во время выполнения.
Плюс, JIT-компилятор имеет в своем распоряжении методы, которые компиляторы AOT не делают, наиболее важным является де-оптимизация. Теперь вы можете подумать, что мы де-оптимизации важны для производительности? Ну, если вы можете де-оптимизировать, тогда вы можете быть чрезмерно агрессивными при создании оптимизаций, которые фактически недействительны (например, встраивание вызова метода, который может быть или не быть полиморфным), и если окажется, что вы ошибаетесь, вы можете затем отмените оптимизацию обратно в un-inlined case (например).
Однако проблема с ресурсами: компилятор AOT может занять столько времени, сколько захочет, и использовать столько памяти, сколько захочет. Компилятор JIT должен украсть свои ресурсы от самой программы, которую пользователь хочет использовать прямо сейчас.
Как правило, это не проблема. Наши сегодняшние машины настолько смехотворно перегружены, что в распоряжении JIT всегда достаточно ресурсов. Тем более, что JIT будет использовать большинство ресурсов, когда сразу вводится много нового кода, что обычно происходит во время запуска программы или когда программа переходит между этапами (например, от разбора файлов конфигурации до настройки графа объектов или от завершения настройки до начала фактической работы), и в это время сама программа, как правило, еще не использует много ресурсов (особенно во время запуска программы). Хорошим примером является Azul JCA. Он имеет 864 ядра и 768 GiByte RAM в своей самой большой конфигурации (и обратите внимание, что они не были проданы в течение довольно продолжительного времени, так что это на самом деле несколько лет технология). Согласно измерениям Азуля, JIT использует, возможно, 50 ядер, когда он работает очень тяжело. Это еще больше, чем 800 ядер для программы, системы и GC.
Но ваше типичное Android-устройство не имеет 1000 ядер и TiByte оперативной памяти. И он чрезвычайно интерактивен и чувствителен к задержкам, когда пользователь начинает, скажем, WhatsApp, он хочет написать сообщение прямо сейчас. Не в 500 мсек, когда JIT разогрелся. ТЕПЕРЬ.
Вот что делает AOT привлекательным здесь. Также обратите внимание, что компиляция JIT будет не только красть ресурсы вдали от запущенной программы, но и будет потреблять энергию батареи, и она будет нужна каждый раз при запуске программы, тогда как компилятор AOT должен будет только потратить этот бюджет мощности один раз, когда приложение установлено.
Вы можете пойти еще более экстремально и вытеснить компиляцию в магазин приложений или даже разработчику, как это делает Apple, но у Apple есть преимущество гораздо более ограниченного набора возможных целевых платформ для рассмотрения, Компиляция AOT устройства кажется разумным компромиссом для Android.