ВМ не имеет ничего общего с языком. Любой язык может работать поверх ВМ (в Java ВМ уже есть сотни языков).
ВМ позволяет запускать другой тип «ассемблера», более подходящий для адаптации компилятора. Все, что делается в ВМ, может быть сделано в ЦП, поэтому думайте о ВМ как о ЦП. (Некоторые на самом деле реализованы аппаратно).
Это чрезвычайно низкий уровень, и во многих случаях он основан на большом количестве стеков - вместо регистров математика на машинном уровне относится к местоположениям относительно текущего указателя стека.
С обычными скомпилированными языками для одного шага требуется много инструкций. a + может выглядеть как «Схватить элемент из точки относительно указателя стека в reg a, перехватить другой в reg b. добавить reg a и b. поместить reg a в место относительно указателя стека.
ВМ делает все это с помощью одной короткой инструкции, возможно, одного или двух байтов вместо 4 или 8 байтов НА ИНСТРУКЦИЮ на машинном языке (в зависимости от 32 или 64-битной архитектуры), что (предположительно) должно означать около 16 или 32 байты x86 для 1-2 байтов машинного кода. (Я могу ошибаться, мое последнее кодирование x86 было в эпоху 80286.)
Microsoft использовала (вероятно, до сих пор) виртуальные машины в своих офисных продуктах, чтобы уменьшить объем кода.
Процедура создания кода виртуальной машины аналогична созданию машинного языка, но, по сути, это другой тип процессора.
ВМ также могут реализовывать свои собственные механизмы безопасности, восстановления после ошибок и памяти, которые очень тесно связаны с языком.
Часть моего описания здесь является краткой и по памяти. Если вы хотите изучить определение байт-кода самостоятельно, это забавно:
http://java.sun.com/docs/books/jvms/second_edition/html/Instructions2.doc.html