Почему Java-приложение весит больше, чем аналог на C
Что проверяют
Отличаете ли вы размер артефакта от потребления памяти и понимаете ли, из чего вообще складывается поставка Java-приложения. Вопрос выглядит простым, но на нём видно, представляет ли кандидат, что именно уезжает в образ вместе с его кодом.
Как отвечать
Разложить вес по слагаемым:
- среда выполнения едет с приложением. Бинарнику на C хватает libc, которая уже есть в системе, а Java нужен JVM и модули стандартной библиотеки внутри образа;
- class-файлы несут метаданные. Constant pool, сигнатуры, имена методов и полей, таблицы исключений, а при сборке с отладочной информацией ещё номера строк и имена локальных переменных. Всё это нужно рефлексии и стектрейсам, поэтому не выбрасывается;
- лишнее нельзя отрезать статически. Линкер C выкидывает недостижимый код и стрипает символы. В Java загрузка класса по имени, рефлексия и внедрение зависимостей не дают доказать, что класс не нужен, поэтому в fat jar библиотека едет целиком;
- зависимости. Основную долю веса дают транзитивные jar фреймворка, а собственный код приложения там меньшая часть.
Хороший ответ заканчивается тем, как вес уменьшают: jdeps и jlink собирают урезанный образ runtime только из нужных модулей, jlink умеет выкинуть отладочные данные, native image в GraalVM даёт один бинарник ценой отказа от произвольной рефлексии без конфигурации, а слоистый образ не уменьшает размер, но перестаёт гонять зависимости при каждом деплое.
Где спотыкаются
Смешивают размер файла и потребление памяти. Рассказ про сборщик мусора и его служебные структуры отвечает на другой вопрос, про RSS процесса: на вес jar сборщик не влияет никак.
Считают, что исключения и проверки утяжеляют артефакт. Блок try компилируется в запись таблицы исключений и стоит байты. Вес дают зависимости и метаданные классов, а не механизм ошибок.
Отвечают общей фразой про тяжесть языка и не могут назвать ни одного способа ужаться. Интервьюер обычно ждёт продолжения: где посмотреть состав артефакта и что реально выкидывается.