Действительно ли CLR является виртуальной машиной?

Другой путь создал бы Ваш собственный шаблонный загрузчик, который загружает шаблоны, характерные для агента пользователя. Это - довольно универсальная техника и может быть использованием для динамичного определения, какой шаблон должен быть загружен в зависимости от других факторов также, как требуемый язык (хороший компаньон к существующему оборудованию Django i18n).

Django Book имеет раздел по этому предмету .

49
задан Aaron Hall 1 March 2016 в 02:15
поделиться

6 ответов

Часть «Виртуальная машина» относится к тому факту, что код .NET компилируется в EXE и DLL как «Промежуточный «Язык ассемблера (IL) для работы на виртуальной машине, в отличие от реального языка ассемблера ЦП. Затем во время выполнения ILM преобразуется в реальную сборку ЦП для выполнения (так называемая JIT-компиляция).

Конечно, вы могли бы написать компилятор .NET, чтобы он был скомпилирован в сборку ЦП язык вместо ИЛ. Однако это не будет переносимо для всех процессоров - вам придется скомпилировать другую версию для каждой пары ОС / ЦП.

7
ответ дан 7 November 2019 в 11:26
поделиться

Преимущество CLR - это свобода писать код на любом языке программирования, который выберет разработчик, поскольку код будет скомпилирован до CLR перед интерпретацией в собственные вызовы. Платформа .NET использует эту JIT-компиляцию для единообразной обработки всего и вывода программ, которые работают на развертываемой платформе, чего нет в компилируемых языках.

2
ответ дан 7 November 2019 в 11:26
поделиться

Я немного придерживаюсь старой школы, поэтому я также называю CLR виртуальной машиной. Я считаю, что среда CLR собирает машинный код из промежуточного байт-кода, что и делает виртуальная машина.

Преимущества CLR в основном связаны с тем, как он собирает машинный код, который использует информацию о типах среды выполнения.

Вы можете разработать собственный фреймворк, столь же мощный, как .NET framework, используя только собственные типы. Единственная гибкость, которую вы теряете, - это возможность пересобирать собственный код, если вы когда-либо переносите свою программу на другую платформу без перекомпиляции.

2
ответ дан 7 November 2019 в 11:26
поделиться

Подобно виртуальной машине Java (JVM) .net CLR представляет собой виртуальную машину, интерпретирующую байтовый код.

JVM интерпретирует программы, содержащие байтовые коды Java и среду CLR .net интерпретирует программы, содержащие инструкции, которые Microsoft называет «промежуточным языком (IL)». Между этими байтовыми кодами есть различия, но виртуальные машины схожи и стремятся предоставить аналогичные функции.

Обе эти реализации виртуальных машин имеют возможность компилировать свой входной байт-код в машинный язык компьютера, на котором они работают. Это называется «Just In Time Compilation (JIT)» , а полученный выходной код называется «JIT-кодом». Поскольку код JIT содержит последовательности инструкций на машинном языке ЦП компьютера, этот код иногда называют «собственным» кодом.

Однако JIT-код качественно и количественно отличается от машинного кода, как объясняется ниже. По этой причине в данной статье JIT-код рассматривается как не что иное, как собственная реализация виртуальной машины при запуске конкретной программы с байт-кодом .

Одна функция, которую обе эти виртуальные машины (ВМ) стремятся предоставить безопасность в форме предотвращения некоторых опасных ошибок программирования. Например, название этого форума веб-сайта, stackoverflow, связано с одним из таких типов опасных ошибок, которые возможны в машинном коде.

Чтобы обеспечить безопасность и безопасность выполнения, виртуальные машины реализуют безопасность типов на «уровне виртуальной машины». Назначения памяти виртуальной машины необходимы для хранения данных, которые хранятся в этой ячейке памяти. Например, если в стек помещено целое число, невозможно извлечь из стека двойное число. «Союзы» в стиле C запрещены. Указатели и прямой доступ к памяти запрещены.

Мы не могли получить те же преимущества, навязывая разработчикам объектно-ориентированную языковую структуру, если результатом является собственный двоичный файл, такой как файл EXE. В этом случае мы не сможем отличить собственные двоичные файлы, созданные с использованием фреймворка, и EXE-файлы, созданные злоумышленником, использующим источники, отличные от фреймворка.

В случае виртуальных машин безопасность типов обеспечивается в «самый низкий уровень», к которому разрешен доступ программисту. (Пренебрегая на мгновение возможностью писать управляемый собственный код, То есть.) Следовательно, ни один пользователь не встретит приложение, которое выполняет одну из опасных операций, требующих прямого доступа к ячейкам памяти и указателям.

На практике .net CLR реализует способ написания собственного кода, который может быть вызван по "управляемому" коду .net. В этом случае автор машинного кода обязан не допускать ошибок указателя и памяти.

Поскольку и JVM, и .net CLR выполняют JIT-компиляцию, либо виртуальная машина фактически создает скомпилированный в машинном коде двоичный файл из байт-кода. поставляется. Этот «код JIT» выполняется быстрее, чем выполнение интерпретатора виртуальной машины, потому что даже код машинного языка, созданный JIT, содержит все необходимые проверки безопасности виртуальной машины, которые она будет выполнять. Как результат, выходной код JIT не такой быстрый, как собственный код, который обычно не содержит многочисленных проверок во время выполнения. Однако этот недостаток быстродействия заменяется повышением надежности, включая безопасность; в частности, предотвращается использование неинициализированного хранилища, обеспечивается безопасность присваиваний по типу, выполняется проверка диапазона (таким образом предотвращается переполнение буфера на основе стека и кучи), время жизни объектов управляется сборкой мусора, динамическое распределение является типобезопасным. Среда, выполняющая такие проверки поведения во время выполнения, реализует спецификацию виртуальной машины и представляет собой не что иное, как реализацию виртуальной машины на машинном языке.

предотвращается использование неинициализированного хранилища, обеспечивается безопасность присваиваний по типам, выполняется проверка диапазона (таким образом предотвращается переполнение буфера на основе стека и кучи), время жизни объектов управляется сборкой мусора, динамическое распределение является типобезопасным. Среда, выполняющая такие проверки поведения во время выполнения, реализует спецификацию виртуальной машины и представляет собой не что иное, как реализацию виртуальной машины на машинном языке.

предотвращается использование неинициализированного хранилища, обеспечивается безопасность назначений по типу, выполняется проверка диапазона (таким образом предотвращается переполнение буфера на основе стека и кучи), время жизни объектов управляется сборкой мусора, динамическое распределение является типобезопасным. Среда, выполняющая такие проверки поведения во время выполнения, реализует спецификацию виртуальной машины и представляет собой не что иное, как реализацию виртуальной машины на машинном языке.

24
ответ дан 7 November 2019 в 11:26
поделиться

Здесь много заблуждений. Я полагаю, вы могли бы думать о .Net как о виртуальной машине, если бы действительно хотели, но давайте посмотрим, как .Net Framework действительно обрабатывает ваш код. Типичный сценарий выглядит так

  1. Вы пишете программу .Net на C #, VB.Net, F # или другом совместимом языке.
  2. Этот код компилируется до промежуточного языка (IL), который похож на Байт-код Java, который распространяется на компьютеры конечных пользователей.
  3. Конечный пользователь запускает программу в первый раз на компьютере с установленной правильной версией .Net
  4. Компьютер видит, что это сборка .Net, а не "сырой" машинный код и передает его JIT-компилятору
  5. JIT-компилятор компилирует IL в полностью собственный машинный код .
  6. Собственный код сохраняется в памяти на время выполнения этой программы.
  7. Вызывается сохраненный машинный код, и IL больше не имеет значения.

Здесь есть пара важных моментов, но самый большой. заключается в том, что никакой код никогда не интерпретируется. Вместо этого вы можете увидеть на шаге 5, что он компилируется в собственный код. Это огромное отличие от загрузки его в виртуальную машину по нескольким причинам:

  1. Полностью скомпилированный код выполняется процессором напрямую, а не интерпретируется или транслируется дополнительным уровнем абстракции программного обеспечения, который должен быть быстрее.
  2. JIT-компилятор может использовать преимущества оптимизации, характерные для отдельной машины, на которой выполняется программа, вместо того, чтобы устанавливать наименьший общий знаменатель.
  3. Если вы хотите, вы можете даже предварительно скомпилировать код и, по сути, полностью скрыть шаг 5 от пользователя.

Я полагаю, вы могли бы назвать это виртуальной машиной в том смысле, что JITter абстрагирует детали реального машина от застройщика. Лично я не думаю, что это действительно так, потому что для многих виртуальная машина подразумевает абстракцию времени выполнения от нативного кода, которого для программ .Net просто не существует.

Еще один ключевой момент во всем этом. Процесс, который действительно отличает его от среды «виртуальной машины», заключается в том, что это всего лишь типичный процесс. Если вы действительно хотите, вы можете предварительно скомпилировать сборку .Net перед распространением и развернуть собственный код непосредственно для конечных пользователей (подсказка: это медленнее в совокупности в течение срока службы программы, потому что вы теряете оптимизацию для конкретной машины). Конечно, вам все еще нужна установленная среда выполнения .Net, но на данный момент она мало чем отличается от любого другого API среды выполнения; это больше похоже на коллекцию dll с красивым API, с которым вы можете связать, как вы могли бы иметь со средами выполнения VB или C, Microsoft также поставляется с Visual Studio. Этот вид убирает ИЛ из поля зрения, что делает оправдание прозвища ВМ намного сложнее. (Я говорю «отчасти», потому что IL все еще развертывается и используется для проверки сохраненного кода, но сам он никогда не задействован для выполнения.)

Еще один важный момент - отсутствие процесса виртуальной машины. Когда вы запускаете приложение, не запускается общий процесс «песочницы». Сравните это с Java, где, если вы откроете диспетчер задач во время работы программы, вы увидите процесс, специально предназначенный для виртуальной машины Java, а фактический процесс приложения - это поток внутри песочницы, созданной виртуальной машиной. В .Net вы видите процесс приложения непосредственно в диспетчере задач Windows.

В итоге: можно сказать, что вместе IL + CLR + JIT каким-то образом составляют виртуальную машину. Лично я так не думаю, но не стану спорить с вами, если вы в это верите. Я хочу сказать, что когда вы говорите кому-то, что .Net работает на виртуальной машине без дополнительных объяснений, идея, которую вы сообщаете этому человеку, является «интерпретируемым байт-кодом в хост-процессе». И это неправильно.

вы видите процесс приложения непосредственно в диспетчере задач Windows.

В итоге: можно сказать, что вместе IL + CLR + JIT каким-то образом составляют виртуальную машину. Лично я так не думаю, но не стану спорить с вами, если вы в это верите. Я хочу подчеркнуть, что когда вы говорите кому-то, что .Net работает на виртуальной машине без дополнительных объяснений, идея, которую вы сообщаете этому человеку, является «интерпретируемым байт-кодом в хост-процессе». И это неправильно.

вы видите процесс приложения непосредственно в диспетчере задач Windows.

В итоге: можно сказать, что вместе IL + CLR + JIT каким-то образом составляют виртуальную машину. Лично я так не думаю, но не буду спорить с вами, если вы в это поверите. Я хочу сказать, что когда вы говорите кому-то, что .Net работает на виртуальной машине без дополнительных объяснений, идея, которую вы сообщаете этому человеку, является «интерпретируемым байт-кодом в хост-процессе». И это неправильно.

Net работает на виртуальной машине без каких-либо дополнительных объяснений, идея, которую вы передаете этому человеку, - это «интерпретируемый байт-код в хост-процессе». И это неправильно.

Net работает на виртуальной машине без каких-либо дополнительных объяснений, идея, которую вы передаете этому человеку, - это «интерпретируемый байт-код в хост-процессе». И это неправильно.

90
ответ дан 7 November 2019 в 11:26
поделиться

Вы получили много ценных ответов, но я думаю, что одна вещь еще не упомянута: модульность.

Довольно сложно экспортировать объектно-ориентированный класс из собственной библиотеки DLL. Конечно, вы можете указать компоновщику экспортировать класс и импортировать его в другое место, но это непросто; Изменение одного закрытого члена в классе нарушит двоичную совместимость, т.е. если вы измените одну DLL без перекомпиляции всех других модулей, ваша программа во время выполнения выйдет из строя.

Есть несколько способов обойти это: Например, вы можете определить общедоступные абстрактные интерфейсы, наследовать их и экспортировать глобальные фабричные функции из вашей DLL. Таким образом, вы можете изменить детали реализации класса. Но вы не можете наследовать от этого класса в другой DLL. И изменение интерфейса, конечно же, нарушает бинарную совместимость.

I ' m не уверен, есть ли хорошее решение для этого в машинном коде: если компилятор / компоновщик создает собственный код во время компиляции, то он должен знать точную структуру памяти классов / структур, которые используются в коде. Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

Если компилятор / компоновщик создает собственный код во время компиляции, он должен знать точную структуру памяти классов / структур, которые используются в коде. Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

Если компилятор / компоновщик создает собственный код во время компиляции, он должен знать точную структуру памяти классов / структур, которые используются в коде. Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

тогда он должен знать точную структуру памяти классов / структур, которые используются в коде. Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

тогда он должен знать точную структуру памяти классов / структур, которые используются в коде. Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев не перевешивают преимуществ.

Если последний шаг компиляции (создание собственного кода) откладывается до первого вызова метода, эта проблема просто исчезает: вы можете изменить класс в сборке, и пока JIT может разрешить все используемые члены во время выполнения , все будет работать нормально.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев не перевешивают преимуществ.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев не перевешивают преимуществ.

В двух словах: если вы создаете монолитную программу с одним исполняемым файлом, вы, вероятно, сможете получить большинство мощных функций .NET с компилятором, который создает собственный код. Но недостатки JIT-компилятора (установка фреймворка, немного более длительное время запуска) в большинстве случаев действительно не перевешивают преимуществ.

1
ответ дан 7 November 2019 в 11:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: