Подтвердить что ты не робот

Обоснование выбора дизайна, заставляющее языки JVM/CLR иметь длительный запуск?

Я рассматриваю разработку языка программирования, и мне бы хотелось, чтобы он запускался с такой же скоростью, как CPython или Perl. Чтобы сделать правильный выбор дизайна на моем языке для достижения этого требования, я смотрю на существующие динамические языки, чтобы увидеть, как их выбор дизайна влияет на время их запуска. У многих языковых реализаций на основе JVM или CLR намного больше времени запуска, чем CPython или Perl. Это говорит о том, что в дизайне JVM и/или CLR был сделан выбор дизайна, который вызывает это. Каким был этот выбор и почему так оно было сделано?

Это трехчастный вопрос:

  • Является ли медленный запуск динамических реализаций языка JVM/CLR основной проблемой дизайна вообще или просто незначительной проблемой, которая может быть исправлена ​​путем улучшения языковых реализаций?
  • Если это проблема дизайна, то , которые выбирают дизайн JVM и какие варианты дизайна этих языков заставляют эти языки иметь более длительную задержку запуска, чем CPython и Perl?
  • Что получается в обмен на медленное начало? То есть, какие преимущества имеют динамические языки JVM/CLR, которые не имеют CPython и Perl из-за выбора дизайна, описанного в (2)?

Обратите внимание, что другие вопросы SO уже касаются "Почему JVM медленно запускается?" и почему разные языки JVM работают медленно. Этот вопрос отличается от этого вопросом, потому что этот вопрос касается проектного компромисса; что получается в обмен на это длительное время запуска?

Другие вопросы SO задают вопрос о том, как пользователь может запускать различные языки JVM (и часто приходится иметь какой-то демон, который предварительно загружает JVM), но это не то, что я прошу здесь; Я спрашиваю, как вы разрабатываете язык (и/или виртуальную машину), который позволяет быстро запускать (без предварительной загрузки) и что вы теряете взамен этого.

Фоновые исследования

Скорость выполнения различных языков

Я тестировал CPython и Perl в неформальных тестах Hello World на моей машине GNU/Linux и обнаружил, что они начинаются менее чем за 0,05 секунды. В остальной части этого сообщения я буду говорить "быстро", чтобы означать "время запуска, которое не намного дольше, чем CPython или Perl", а "медленное" означает иначе.

Легко найти мнения о том, что сам JVM и/или Java не запускаются (3, 4, 5, 6), а также конкретные номера порядка 1 секунды или более (7, 27) и контрольных показателей (8). Однако два теста Hello World JVM начались всего за 0,04 секунды (в GNU/Linux) (9, 10).

Clojure было время запуска около 0,6-1 секунды (1, 2); это примерно в 20 раз медленнее, чем моя цель 0,05 секунды. ClojureCLR еще медленнее (1). Clojure Тесты времени запуска и обсуждения можно найти в сообщениях блога Clojure bootstrapping (Kariniemi), Почему Clojure slow (Trojer).

Один контролер времени запуска сказал, что Clojure и JRuby были "значительно медленнее, чем все остальное" (25); они также были проверены только на двух динамических языках, основанных на JVM. Другой (очень старый) тест показывает, что Jython также очень медленно запускается (26). Мы фокусируемся на динамических языках в этом вопросе, однако может быть уместным, что Scala также невероятно быстро (1). Существует схема JVM, называемая Kawa (23). Сообщалось, что время запуска Kawa около 0,4 (24), которое быстрее, чем Clojure, но все же порядка величины выше моей цели.

Каковы реализации, выполняемые при запуске?

Оба (1, 2) заключают, что Clojure проводит время загрузки классов загрузки и инициализирует пространство имен clojure.core. Ответ на вопрос SO "Clojure производительность запуска приложения", похоже, говорит о том, что различие между временем запуска Java и временем запуска Clojure связано с тем, что Java лениво загружает свои стандартная библиотека, тогда как Clojure загружает ее с нетерпением. Ответы на вопрос SO "Может ли какая-либо реализация Clojure запускаться быстро?" включить "это просто проблема с реализацией, которая может быть исправлена, а не фундаментальный выбор дизайна" (перефразировать) и "Одно из ограничений JVM заключается в том, что объекты должны быть скопированы при инициализации," вы не можете вставлять какие-либо составные константы в байтовом коде. Даже массивы. "").

В одном сообщении сообщается, что время запуска ClojureCLR в основном проводится JITing, а pre-JITing резко сокращает время (хотя оно все еще может быть медленным по сравнению с CPython и Perl).

В одном объяснении, почему некоторые JVM или Java-программы запускаются медленно, это ввод/вывод загрузки во многих файлах классов из стандартной библиотеки (11). Эта гипотеза подтверждается результатами тестов, которые показывают резкое улучшение времени запуска JVM для "теплых запусков", где, по-видимому, содержимое стандартных файлов классов библиотеки уже загружено в кэш операционной системы. Некоторые говорят, что большая часть времени запуска связана с чтением ввода-вывода в файлах классов, но не из-за огромного объема данных, а из-за субоптимальной организации этих данных на диске (15, 16).

JVM верификатор байтов, вероятно, не является существенным фактором времени запуска, потому что ускорение на 40% верификатора переводится только на 5% -ное ускорение большого времени запуска программы (14).

Какие варианты дизайна (не) приводят к медленному запуску?

В (22), Kariniemi приходит к выводу, что запуск Clojure по своей сути медленно начинается из-за выбора дизайна, включая динамические функции. Тем не менее, я сомневаюсь в этом, потому что CPython и Perl добиваются гораздо более быстрого запуска, сохраняя при этом динамизм.

Использование байт-кода не может быть причиной, потому что CPython также использует байт-код.

Поскольку ввод/вывод файлов классов загрузки, похоже, виноват, можно предположить, что основной выбор дизайна - предоставление большой стандартной библиотеки. Однако это не может быть причиной, потому что CPython также предоставляет большую стандартную библиотеку и не запускается медленно. Кроме того, хотя медленность Java находится под сомнением, стоит отметить, что Java должен загружать rt.jar при запуске, но Hello World быстро запускается на Java в соответствии с некоторыми критериями.

4b9b3361

Ответ 1

Время запуска определяется количеством работы, необходимой для времени выполнения, прежде чем он может фактически начать выполнять любой "код пользователя". Позвольте мне сравнить, что именно происходит с некоторыми вариантами.

Native Binary (С++ или около того)

Операционная система отображает главный исполняемый файл в памяти. Даже если этот файл очень большой (несколько ГБ), отображение все еще довольно быстро. И это очень быстро для типичного размера файла 10-50 МБ. Затем считывается некоторый исполняемый заголовок, который предоставляет список динамических модулей. Эти модули просматриваются ОС и отображаются таким же образом. Затем, возможно, происходят некоторые перемещения. После этого ваш код будет готов к выполнению (хотя управление на этом этапе, вероятно, дается языковой среде исполнения, а не самому коду).

Языки сценариев

После того, как все описанное в предыдущем разделе происходит с исполняемым файлом интерпретатора, он начинает считывать и выполнять при условии script. Предположим, что не выполняется синтаксический анализ/компиляция в байт-код (у нас уже есть все в формате .pyc или аналогичном байт-коде). Чтобы каждый модуль-интерпретатор нуждался в загрузке, он просто выделяет достаточно долгое количество памяти и копирует в него содержимое модуля. Затем он передает управление этому фрагменту байт-кода. Некоторая работа действительно должна быть сделана на этом этапе, но обычно это не очень много. Например, thats байт-код модуля python dis, который будет выполнен в import dis.

Переход к JVM

Для JVM это не так просто. Во-первых, среда выполнения не может просто "отображать" .class файл в память, а также не считывать его содержимое в память и не говорить интерпретатору: "Эй, вот ваш байт-код". Он должен быть проверен и разрешен.

Цель проверки состоит в том, чтобы убедиться, что интерпретатор может выполнять без каких-либо дополнительных проверок времени выполнения (ветки вне работы, переполнение стека или переполнение стека, проверка типов). Даже если мы предполагаем, что O (количество инструкций) привязано к проверке, это все равно в значительной степени, так как каждая отдельная команда в модуле должна быть проверена. Помните, что для языка сценариев мы выполняем небольшую работу по загрузке, обычно просто для заполнения словаря "export" новыми функциями и классами.

Resolve - это своего рода оптимизация (и выбор языка). Рассмотрим Java-код:

System.out.println("Hello, world!");

Для этого кода java-компилятор помещает в .class файл информацию о println: println является статическим методом с сигнатурой (ILJAVA/LANG/STRING;)V из класса java.lang.System. Когда класс, содержащий указанную выше строку, загружается, JVM должен искать java.lang.System (возможно, загрузить его также в процессе), найти метод println с этой сигнатурой и поместить указатель на этот метод где-нибудь, чтобы он мог быть позже найден, когда эта строка выполняется.

Эта процедура должна выполняться для каждого уникального вызова метода в каждом загруженном классе. То же самое для каждого ссылочного класса, поля, интерфейса и т.д. Поэтому загрузка большого файла .class не связана с "копированием его содержимого в память" и "выполнением некоторой настройки окружения".

При достаточно большой стандартной библиотеке одни только эти операции могут привести к длительному времени запуска.

Компиляция (по крайней мере оптимизация компиляции) выполняется медленно. Помните, сколько времени может понадобиться для компиляции проекта С++ для достойного размера. Таким образом, различные трюки использовались для ускорения этой операции. В JVM (по крайней мере, в некоторых реализациях) интерпретация и компиляция JIT могут выполняться параллельно, поэтому код интерпретируется по умолчанию и JIT'ed, если он определен как "горячий" (выполняется часто). Но интерпретация также медленная. Так что это не волшебная пуля, просто компромисс между "тем, что делаете медленно" или "не делает их вообще, и надеюсь, что JIT скоро закончит свою работу". Python без поддержки jit просто "делает это медленно". Но некоторые критически важные компоненты стандартной библиотеки (например, словари) написаны на C (или Java или С#, а не на самом Python). Стандартная Java-библиотека написана на Java. Именно поэтому он также должен быть скомпилирован JIT или будет работать медленно.

Резюме

  • Эти медленные времена запуска - проблема дизайна. Это цена будучи "почти таким же быстрым, как C" и очень динамичным одновременно.

  • Выбор дизайна, приводящий к этому замедлению, - это: проверка байт-кода   во время загрузки вместо времени выполнения, привязки времени загрузки и JIT   сборник.

  • Как я уже сказал, это позволяет JVM генерировать код с JIT, который       почти так же быстро (и даже быстрее на некоторых тестах), чем код на родном языке.

Заключение

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

Накладные расходы JIT могут быть избиты с помощью JIT-кеша, или компиляция Ahead-of-Time. Это не так, если ваш язык полностью динамичен (например, вы можете переопределить свойство array.length в некотором модуле, а стандартная библиотека также должна уважать это изменение).

Ответ 2

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

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

  • Время компиляции
  • Инициализация
  • Время выполнения

Языки, которые обычно скомпилированы, например, C, проводят много времени в (1) для оптимизации генерируемого двоичного файла. Компилятор может сделать одну из двух вещей: либо он пытается сделать двоичный файл очень маленьким, поэтому, когда программа запущена, она быстро загружается из жесткого диска в ОЗУ, тем самым минимизируя время инициализации (2). Но чаще всего компиляторы обычно не беспокоятся о бинарном размере и вместо этого пытаются оптимизировать производительность во время выполнения (3). Вставка функций - это классический пример оптимизации производительности во время выполнения за счет двоичного размера.

На другом конце код на языках, которые обычно интерпретируются, например JavaScript или даже скрипты Bash, практически не имеют этапа компиляции (поэтому они сводят к минимуму (1) до нулевого времени). Затем они также должны решить между оптимизацией (2) и (3). Традиционно интерпретируемые языки загружали только среду выполнения, а затем начали интерпретировать код приложения, но появление JIT (компиляция "точно вовремя" ) несколько изменило ее, улучшив производительность рабочего времени (3) за счет потенциально более длительной инициализации (2).

Чтобы конкретно ответить на ваш вопрос: что получается в обмен на то, чтобы начать медленно? В первую очередь производительность исполнения. Раньше это было проблемой, когда компьютеры были намного медленнее. В случае с JVM/CLR достигается переносимость (если у вас есть JVM, вы можете запускать тот же код в архитектуре x86 и ARM).


Функциональные языки, такие как Clojure и Haskell, имеют несколько иные проблемы для решения, чем упомянутые выше императивные языки. Например, Haskell встраивает свою небольшую рабочую среду в каждый бинарный файл, генерируемый компилятором Haskell. Это позволяет, среди прочего, использовать Haskells ленивую оценку семантики. Таким образом, вопреки тому, что ранее говорилось в Ive, в дизайне функционального языка, дизайн языка (и не только компиляция/исполнение) может очень сильно повлиять на необходимое время инициализации программы.

Однако, если вы собираетесь написать компилятор или интерпретатор, я бы рекомендовал начать с императивного языка, поскольку компиляция функционального кода концептуально намного сложнее. Если вам нравятся книги, то многие Книга Дракона находятся в хороших отношениях.

Ответ 3

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

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

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

Примером не фундаментального, но неудачного выбора дизайна является выбор java для одного класса на один файл, который был задуман еще в тот день, когда java предназначалась для работы внутри веб-страниц на стороне клиента: это значительно уменьшает время запуска одного класса, но при этом каждый раз, когда вы делаете то, что требует загрузки большего количества ваших классов, оно вводит огромное дополнительное ограничение производительности. К тому времени, когда вы загрузили достаточно классов, чтобы завершить свой стартап, вы, как правило, заплатили гораздо больший общий штраф за производительность, чем если бы вы ждали, чтобы загрузить все эти классы сразу.

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

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

  • Использование сборщика мусора. Это довольно сложная часть программного обеспечения, поэтому я не удивлюсь, если у нее будет несколько дорогостоящая инициализация. Например, сборщик мусора обычно работает на своем потоке, что означает, что запуск приложения обременен запуском хотя бы одного потока. (Который должен иметь свой собственный стек и т.д.). Это можно оптимизировать, запустив дополнительный сборщик мусора только в том случае, если это необходимо, поэтому приложения, которые заканчиваются, прежде чем выделять много памяти, никогда не нуждаются в нем. Кроме того, многие приложения GUI теоретически могут выполнять всю сборку мусора во время простоя, поэтому для этого на самом деле не нужен дополнительный поток, хотя время запуска графических приложений обычно не вызывает особой озабоченности.

  • Использование изолированной виртуальной машины. Это означает, что сразу после запуска виртуальная машина должна предварительно выделить огромный кусок памяти для работы. Массивное распределение памяти обычно приводит к значительному снижению производительности, но это избавляет нас от дополнительных распределений небольших блоков, которые могут быть добавлены к значительному снижению производительности в долгосрочной перспективе. Обычно это проблема с операционной системой/аппаратной архитектурой, и я не удивлюсь, если Linux сделает гораздо лучше, чем Windows. Один из способов его оптимизации состоит в том, чтобы установить какой-то способ определения конца вашего времени запуска, принимая во внимание, сколько требуется памяти, а затем при последующих запусках указывает виртуальной машине на выделение именно такой большой памяти: не меньше, чтобы не нанести штраф дополнительных ассигнований и не более того, чтобы не тратить время на выделение памяти, которая не понадобится. Конечно, вам нужно быть умным, потому что память, которую приложение вам нужно будет выделять при запуске, часто зависит от аргументов командной строки, которые, вероятно, будут отличаться от run to run. Как обычно, нет серебряной пули.

  • Вопросы безопасности. Это область, на которой у меня мало опыта, поэтому я не могу дать много понимания. Я знаю, что когда моя java-программа запускается, каждый байт моего байт-кода проверяется некоторым верификатором, и я знаю, что каждый раз, когда я пытаюсь сделать что-то невинным, как загрузку класса, некоторый менеджер безопасности вызывается, чтобы узнать, должно быть разрешено сделать это. Для жизни я не знаю, почему JVM беспокоится обо всем этом, а не просто позволяет операционной системе обрабатывать процесс, который выходит из строя, или процесс, который не имеет достаточных привилегий для выполнения своей работы. Я несколько раз понимаю, что это не что иное, как маркетинговый план, чтобы добавить "модное слово безопасности" как одну из точек продажи языка, но, конечно, только в теге <joking></joking>. Я сильно подозреваю, что вы можете покончить со всей этой "безопасностью", но обязательно проконсультируйтесь с другими в этой теме.

  • Организация файлов классов. Java использует JAR файлы, которые по существу являются ZIP файлами, поэтому в первый раз, когда требуется класс из файла JAR, весь файл должен анализироваться и, возможно, все его распаковывать и индексировать, даже если никакой другой класс никогда не понадобится Это. Я уверен, что JVM оптимизируют это как можно больше, но, тем не менее, я готов поспорить, что другой выбор может дать лучшие результаты. Разумеется, преимущество состоит в том, что вы можете открыть свои файлы JAR с помощью утилиты ZIP. CLR использует сборки DLL, структуру которых я не знаю, но я был бы готов поспорить, что они выполняют (или могут потенциально выполнять) лучше.

  • Основополагающее решение предоставить богатую среду выполнения и культуру программирования для повторного использования существующих функций для решения проблем вместо предоставления оптимальных, но ad-hoc-решений. В C, ваш "Привет, мир!" программа будет передавать функцию printf() указатель на статический массив символов. Ничто не может работать лучше, чем это. В Java будет создан экземпляр String, дополнительная память будет выделена для массива символов, символы будут скопированы в эту память, а затем указатель на строковый объект будет передан функции System.out.println(). Класс String довольно сложный, и большая часть его сложности связана с производительностью после запуска. (См. Интернирование строк). Тогда функция println(), вероятно, будет преобразовывать символы unicode в ansi, что означает, что будут загружены различные классы преобразования текста, вполне возможно, наряду с таблицами преобразования в unicode, которые имеют незначительный размер, Эти классы преобразования текста, скорее всего, будут использовать стандартные коллекции, где только HashMap составляет около 50 КБ байт-кода. Вы видите, где это происходит.

Ответ 4

Вот краткое изложение некоторых других ответов.

Возможные проектные компромиссы:

  • время ожидания и пропускная способность
  • предварительная компиляция некоторых библиотечных модулей по сравнению с std-библиотекой только в байт-коде.
  • lazy load module import
  • безопасность и эффективность
  • эффективность организации модулей на диске

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

Подробнее:

латентность и пропускная способность

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

  • загрузка времени сбор (или частичная компиляция)
  • привязка времени загрузки
  • загружать стандартную библиотеку
  • возможно, выполните дополнительную работу при инициализации GC

JVM действительно связывает время загрузки (deniss). Многие реализации JVM выполняют частичную компиляцию во время загрузки или инициализации (deniss, mb21). CPython предоставляет большую стандартную библиотеку, но только предварительно загружает эти модули, необходимые во время запуска (delnan); 14 подразумевает, что Java лениво загружает свою стандартную библиотеку, но Clojure с нетерпением загружает ее. Майк Накис предполагает, что JVM может потратить много времени на инициализацию GC.

предварительная компиляция некоторых библиотечных модулей по сравнению с std-библиотекой только в байт-коде, только интерпретированная и отдельная от основного исполняемого файла

Существуют различные способы предварительной компиляции набора библиотечных модулей:

  • их байт-код может быть скомпилирован во время загрузки
  • среда выполнения может однажды загружать набор библиотек, выгружать образ своей памяти, а затем в следующий раз просто загружать в это изображение
  • они могут быть скомпилированы в основной исполняемый файл VM
  • они могут быть написаны на другом языке и скомпилированы в двоичные библиотеки, которые VM динамически загружает во время выполнения

Помимо повышенной сложности реализации, я не могу придумать никаких компромиссов по дизайну с первыми тремя из них. Последнее связано с компромиссом, поскольку отказ от собственного кода и запись стандартной библиотеки на языке имеет другие преимущества: (a) переносимость (b) разработчикам не нужно знать, как писать собственный код, чтобы внести свой вклад в стандартную библиотеки (c) использование стандартной библиотеки в качестве образца кода (d) для стандартных библиотек не требуется FFI, и они не требуют специального специального лечения (как если бы они были родными, а другие библиотеки не могли быть).

CPython поставляется с некоторыми стандартными библиотечными модулями, скомпилированными в основной исполняемый файл (delnan). Некоторые реализации JVM предоставляют " обмен данными с классами", который, как представляется, является системой для кэширования изображения тех частей стандартной библиотеки Java, которые являются всегда загружается при запуске. Однако совместное использование данных класса JVM "по-видимому, не может быть легко использовано для прекомпиляции пользовательских библиотек, таких как стандартные библиотеки Clojure, что означает, что другим языкам JVM, кроме Java, не повезло. Некоторые другие языки, такие как SBCL, предоставляют средство для сброса изображения текущего состояния интерпретатора ( 20).

импорт ленивого загрузочного модуля

Ответ на вопрос SO "Clojure производительность запуска приложения" , похоже, говорит о том, что различие между временем запуска Java и временем запуска Clojure связано с тем, что Java лениво загружает библиотечные модули, тогда как Clojure загружает его с нетерпением. За исключением повышенной сложности (что важно), я не могу думать о причине дизайна, почему любой язык не должен поддерживать (по крайней мере, необязательную) ленивую загрузку модулей.

безопасность и эффективность

Язык может пожелать предоставить "песочницу", в которой ненадежный код может быть выполнен с ограниченными возможностями. Для этого требуется либо проверка кода перед его запуском, либо динамическая вставка проверок перед потенциально ограниченными действиями (Майк Накис). В последнем случае, если одно из этих действий заключается в загрузке модуля, эта динамическая проверка может выполняться много раз при запуске, добавляя к времени запуска.

Аналогично, язык может пожелать гарантировать, что даже байт-код, написанный противником, не может "сбой" неконтролируемым образом, который возвращает управление ОС, но в худшем случае может вызвать только исключение, передав управление через язык обработчик исключений. Для этого требуется либо язык байт-кода, который не может синтаксически выражать ситуации, вызывающие сбои, такие как незаконный доступ к памяти, или требует выполнения динамических проверок во время выполнения перед каждым потенциально опасным действием или требует проверки байт-кода во время загрузки, чтобы доказать, что он не может вызвать сбой (deniss). Последний из этих вариантов увеличивает время загрузки каждого модуля, добавляя к времени запуска.

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

эффективность организации модулей на диске

Библиотеки JVM распространяются по многим файлам, потому что они являются одноклассниками для каждого файла. Это увеличивает время загрузки по сравнению с помещением многих классов в один файл ( 5, Майк Накис). Библиотеки JVM также кодируются в .jar файлы, которые используют формат ZIP файла. Другой формат файла может быть более эффективным для загрузки, особенно в тех случаях, когда требуется только несколько классов из большого .jar со многими классами (7, Майк Накис).

Кроме того, байт-код JVM не поддерживает составные константы в байт-кодексе, что означает, что составные константы должны быть созданы во время выполнения из последовательности интерпретируемых команд, выполняемых при инициализации. (однако, я думаю, что CPython является таким же образом). Разрешение составных констант в байт-коде должно требовать, чтобы спецификация VM должна была включать формат для сериализации составной структуры данных, что значительно увеличило сложность спецификации.