Зачем нужен байт-код?
Особенность многих jit компиляторов в том что они умеют оптимизировать код на лету, используя статистику выполнения программы,
например hotpath оптимизация считает количество попаданий в ту или иную часть программы, и генерит машинный код только для кусков кода где программа реально часто выполняется. 
Что это дает: за счет этого jit оптимизатор может разместить куски часто выполняющегося  машинного кода очень близко друг к другу - так что они все целиком будут умещаться например в кэше процессора, и да - jit компилятор порой за счет этого обгоняет прекомпилированный машинный код.
Есть еще куча оптимизаций например касающаяся языков которые поддерживают closures, как показывает практика большинство клозур используются в коде с одинаковыми переменными окружения, что позволяет не выполнять кучу работы по сохранению окружения и тп - а просто заинлайнить клозуру - другое дело что на этапе компиляции понять это невозможно, а вот на этапе выполнения сохранить hash окружения и если он не меняется то инлайнить код  - легко
Есть еще куча подобных оптимизаций которые реально помогают динамическим языкам работать почти наравне по скорости с С на некоторых задачах, яркий пример luajit
И до кучи динамические языки зачастую невозможно заранее перенести в native код чтобы сам этот код не прератился в некорый интерпретатор байткода, вот хорошо про это написано: 
stackoverflow.com/questions/15626611/can-regular-j...
(c# кстати нединамический поэтому для него насколько я помню была какая то тулзень для прекомпиляции в native код - но я уже лет сто ;-) не писал на c# поэтому точно не помню)
Идея следующая. Берется программа, транслируется в платформонезависимый байткод. Затем под виртуальной машиной этот байткод можно запустить без изменений и под x86 Windows и под x64 Linux. То есть, программу не придется компилировать под каждую платформу отдельно. На самом деле я даже могу не знать о существовании некоторых платформ, на которых мою программу кто-то запустит. Возможно, этих платформ еще даже не существует.
Кроме того, при обновлении виртуальной машины байткод может быть скомпилирован в более эффективный машинный код. И вообще-то даже на двух процессорах с одной архитектурой могут быть применимы очень разные оптимизации, так что платформ в действительности сильно больше, чем количество архитектур * количество ОС.