(Это чрезвычайно новый вопрос).
Я немного изучал виртуальные машины.
Оказывается, многие из них спроектированы очень похоже на физические или теоретические компьютеры.
Я читал, что JVM, например, является «машиной стека». Это означает (и исправьте меня, если я ошибаюсь), что он хранит всю свою «временную память» в стеке и выполняет операции с этим стеком для всех своих кодов операций.
Например, исходный код 2 + 3
будет преобразован в байт-код, подобный следующему:
push 2
push 3
add
У меня вопрос такой:
JVM, вероятно, написаны с использованием C / C ++ и тому подобного. Если так, почему JVM не выполняет следующий код C: 2 + 3
..? Я имею в виду, зачем ему стек или другие регистры виртуальных машин - как на физическом компьютере?
Основной физический процессор заботится обо всем этом. Почему пишущие виртуальные машины просто не выполняют интерпретируемый байт-код с «обычными» инструкциями на языке, на котором программируется виртуальная машина?
Зачем виртуальным машинам эмулировать аппаратное обеспечение, если реальное аппаратное обеспечение уже делает это для нас?
Опять же, очень новые вопросы. Спасибо за вашу помощь
источник
printf("hi");
: считается ли это виртуальной машиной ? У него нет ни «стека», ни «регистров», ни чего-либо еще.Ответы:
Машина, виртуальная или нет, нуждается в модели вычислений, которая описывает, как вычисления выполняются на ней. По определению, как только он вычисляет, он реализует некоторую модель вычисления. Тогда возникает вопрос: какую модель выбрать для нашей виртуальной машины? Физические машины ограничены тем, что может быть эффективно и действенно сделано аппаратно. Но, как вы заметили, виртуальные машины не имеют таких ограничений, они определяются в программном обеспечении с использованием языков произвольно высокого уровня.
На самом деле, как вы описываете, существуют виртуальные машины высокого уровня. Они называются языками программирования . Например, стандарт C посвящает большую часть своих страниц определению модели для так называемой «абстрактной машины C», которая описывает, как ведут себя программы на C, и в качестве расширения (как правило), как соответствующий компилятор C (или интерпретатор). должен вести себя
Конечно, мы обычно не называем это виртуальной машиной. Под виртуальной машиной обычно понимается нечто более низкое, более близкое к аппаратному, не предназначенное для непосредственного программирования, предназначенное для эффективного выполнения. Это смещение выбора означает, что то, что принимает высокоуровневый составной код (например, то, что вы описываете), не будет считаться виртуальной машиной, поскольку выполняет высокоуровневый код.
Но чтобы перейти к сути, вот несколько причин сделать виртуальную машину (например, что-то, предназначенное для компилятора байт-кода) регистровой или тому подобное. Укладывать и регистрировать машины чрезвычайно просто. Для каждой инструкции есть последовательность инструкций, некоторое состояние и семантика (функция State -> State). Нет сложных сокращений дерева, нет приоритета оператора. Синтаксический анализ, анализ и выполнение очень просты, потому что это минимальный язык (синтаксический сахар компилируется) и предназначен для машинного чтения, а не человеческого чтения.
Напротив, анализ даже самых простых C-подобных языков довольно сложен, и для его выполнения требуются нелокальные анализы, такие как проверка и распространение типов, разрешение перегрузок, поддержка таблицы символов, разрешение строковых идентификаторов, превращение линейного текста в AST на основе приоритетов. , и так далее. Он основан на концепциях, которые естественны для человека, но должны быть тщательно обработаны машинами.
Байт-код JVM, например, излучается
javac
. Его практически никогда не нужно читать или писать людям, поэтому естественно ориентировать его на потребление машинами. Если вы оптимизировали его для людей, виртуальная машина будет только при каждом запуске читать код, анализировать его, анализировать это, а затем преобразовать его в промежуточное представление , напоминающее такая модель упрощенной машины в любом случае . С таким же успехом можно вырезать среднего человека.источник
System.out.println("hi");
есть компилируется в какую-то инструкцию в стеке,int a = 7
компилируется в инструкцию в стеке и т. Д.) Делает выполнение программы простым и более эффективным?2 + 3
компилируется вpush 2 push 3 add
.add
Шаг в конце выполняется с помощью виртуальной машины Java в любом случае, запустив код C2 + 3
. У программистов JVM нет другого способа сделать это. Почему бы не скомпилировать его2 + 3
и заставить JVM просто выполнить код C2 + 3
(если он написан на C) прямо сейчас?2 + 3
исходный код JVM, потому что JVM должен работать с любой программой, выполняющей любые операции в любом порядке. Создание исходного кода на C и откладывание до реализации на C просто выдвигает ту же проблему в реализацию на C (и не может быть сделано легко, не говоря уже об эффективности). Должна существовать некоторая структура данных, описывающая программу, чтобы ее можно было интерпретировать и скомпилировать JIT, а «читаемый исходный код» является ужасным выбором структуры данных по причинам, изложенным выше.a + b
? Тогда добавляемые значения не приходятi.argument{1,2}
, они загружаются из локальных переменных. Как насчетfrobnicate(x[i]) + (Foo.bar() * 2)
? Используя этот дизайн, есть только однаadd
операция (дляint
), и она работает независимо от того, как были вычислены добавления. Плюс, инструкция, которая добавляет только целочисленные литералы, была бы бессмысленной: ее результат также можно было бы предварительно вычислить (т. Е. Вместоadd(2,3)
этого должен бытьpush(5)
).Этот ответ фокусируется на JVM, но на самом деле он применим к любой виртуальной машине.
Это не так, но это делает виртуальную машину намного проще и портативнее: виртуальная машина, которая эмулирует аппаратное обеспечение, может использовать ту же вычислительную модель, что и любой аппаратный процессор.
В частности, JVM была разработана с учетом переносимости, фактически она была построена таким образом, чтобы ее можно было даже реализовать на аппаратном уровне (сегодня может быть трудно поверить, но происхождение Java было во встроенном мире - в частности, контроллеры для интерактивного телевидения). ).
Если у вас есть цель, подобная этой, желательно, чтобы виртуальная машина работала как можно ближе к физической машине, поскольку преобразование в реальный машинный код становится проще и, следовательно, быстрее. Теоретически, когда у вас есть коды операций виртуальной машины, все, что вам нужно сделать, это перевести на коды операций процессора, на котором фактически работает программа. На практике все не так просто.
Преимущество использования модели виртуальной машины, основанной на стеке, заключается в том, что ее можно легко переносить как на машины регистрации, так и на машины стека, тогда как обратное не всегда верно. Виртуальная машина на основе регистров должна делать предположения о количестве регистров, размере регистров и т. Д. При использовании стекового компьютера такие предположения не требуются.
Ну, вот что делают такие виртуальные машины, они интерпретируют байт-код. Даже JVM фактически делает это, по крайней мере, до того, как JIT (как раз вовремя) начинает работать: он интерпретирует байтовые коды и выполняет операторы на языке, на котором написана JVM (обычно C или C ++, но есть даже один написанный в JavaScript, Доппио ). Обратите внимание, однако, что даже такие операторы были преобразованы в машинный код компилятором и на самом деле выглядят очень похоже на то, что производит компилятор Java, а именно они используют регистры и стек для выполнения своей работы. Обратите внимание, что использование «интерпретированных» и «скомпилированных» языков становится несколько размытым в этой точке.
источник
Почему виртуальные машины должны быть «стековыми машинами» или «регистрировать машины» и т. Д.?
Они не. Если вам нужна виртуальная машина, это может быть что угодно.
Существующие виртуальные машины появились в качестве решения таких ситуаций, как: мне пришла в голову действительно блестящая идея, я изобрел новый язык программирования! Но я должен генерировать код. (Какая скучная задача!) Но я не хочу генерировать код i8086, потому что он уродлив, и я не хочу генерировать код 68k, потому что все остальные используют Intel. Также есть VAX, но у меня нет ни VAX, ни компьютера, ни книги по VAX. Поэтому я сгенерирую код для некоторого процессора, который физически не существует, и реализую этот процессор в программном обеспечении. Спецификация этой ВМ станет главой в моей диссертации. Теоретически, будет возможно скомпилировать его для нативного кода любого процессора, но это не я.
С другой стороны, обозначения типа «2 + 3», вероятно, не будут использоваться виртуальными машинами в обозримом будущем, потому что они предполагают выполнение большого количества преобразований, прежде чем что-либо может быть выполнено.
источник
Чтобы ответить на фактический вопрос, который был задан. Термин «виртуальная МАШИНА» означает, что ВСЕ программное / аппаратное обеспечение моделируется / эмулируется. Если вы используете основное программное / аппаратное обеспечение для выполнения инструкций, то у вас нет виртуальной машины, у вас есть компилятор / интерпретатор.
источник