В чём существенная разница между Go runtime, .net CLR и JVM?

В чём существенная разница между этими средами выполнения в плане безопасности и производительности?


Ответы (1 шт):

Автор решения: Pak Uula

Нельзя сравнивать "безопасность и производительность", так как налажать можно в любом рантайме. Сравнивать можно по другим аспектам. Как минимум:

  • представление кода для распространения, трансформация кода во время выполнения.
  • управление памятью,
  • управление параллельностью.

Как распространяются программы

  • Go: бинарник в машинных инструкциях, оптимизированном под конкретную аппаратную архитектуру и системные библиотеки. Бинарник не требует, чтобы в системе были установлены какие-то особые Go библиотеки. Требуется только glibc/libc для linux/embedded или msvcrt для windows. Бинарный код во время выполнения не меняется.

  • Java: бинарник jar с файлами машинно-независимого байткода java. Для запуска требуется интерпретатор Java (JVM). Без JVM запуск невозможен. Байткод один и тот же для всех целевых платформ. Во время выполнения рантайм может транслировать байткод в машинный код (JIT, Just In Time compilation).

  • .net: несколько разных форматов

    • канонический - DLL с бинарным представлением платформенно-независимого языка IL (Intermediate language). Для запуска необходим CLR. При выполнении приложения IL транслируется в машинный код (JIT).
    • ReadyToRun, в IL DLL добавляется опциональный машинный код целевой платформы. Если платформа совпадает с целевой, запускается машинный код, в противном случае выполняется как IL приложение. CLR необходим.
    • Ahead-of-Time компиляция: DLL/EXE в машинном коде целевой платформы. CLR не требуется. Есть ограничения на API (например, Reflection)

Управление памятью

  • Go: параллельный алгоритм mark-and-sweep, без уплотнения и поколений. Старается не останавливать выполнение программы для сборки мусора. Может приводить к фрагментации памяти. Аллокатор автоматически решает, где разместить объект - на стеке или в куче. При компиляции используются агрессивные алгоритмы escape analysis для определения времени жизни объектов и быстрого удаления временных объектов.

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

  • .net: внутри аналогичен Java, снаружи есть два варианта - Workstation vs Server. Workstation стремится ограничить размер кучи, и гарантирует интерактивность приложения (без зависаний). Server заточен под производительность, поэтому память может разбухать из-за Gen2 объектов, для удаления которых используются консервативные стратегии в духе "не удали лишнего".

Параллельность

  • Go:легковесные потоки горутины. Горутины дирижируются рантаймом, могут перекидываться из потока (thread) в поток. Каждая горутина имеет свой стек, для взаимодействия использутся каналы. Возвращаемое значение горутины теряется. Нет и не будет futures / async / await

  • Java: изначально поддерживались OS Threads, в последних версиях JVM появились виртуальные потоки (virtual threads) дирижуемые рантаймом, и futures. async/await нет, нужно явно ждать future.

  • .net: изначально поддерживались OS Threads, сейчас поддерживаютя Task/await. Легковесных потоков (как горутины) в .net ИМХО нет.

Как то так.

Это совсем не то, что вы спрашиваете "безопасность и производительность"

→ Ссылка