Как запустить java приложение без java
Одна из основных трудностей распространения Java программ – необходимость наличия на компьютере пользователя среды выполнения Java Runtime Environment (JRE). Именно поэтому переносные (portable) приложения на Java редкость.
В тоже время эту проблему вполне можно разрешить и создать не только переносное, но и «обычное» приложение, которое не будет требовать наличия предустановленной JRE.
Однако, учитывая кроссплатформенность Java, сразу отметим, что в данной статье речь пойдёт о решении данной задачи применительно к среде Windows.
Существует два основных подхода к решению данной задачи.
Способ 1. Преобразование в нативный код
Иначе говоря, преобразование из jar в exe.
Казалось бы, это самый очевидный способ. Но, на самом деле, он самый трудно реализуемый и практически не работоспособный.
Сконвертировать jar в exe в принципе не проблема. В частности, есть масса утилит, которые позволяют это сделать. Однако подавляющее практически все они не избавляют от необходимости устанавливать JRE. Без JRE exe файл, созданный этими утилитами, всё равно не запустится.
Те же из них, что, если верить описанию, претендуют на полное решение данной проблемы, давно не развиваются и перед применением их необходимо самих компилировать из исходных кодов.
Способ 2. Использование переносной версии JRE
Этот способ основан на том, что JRE может работать и без установки на компьютер. Достаточно скачать архив с официальной страницы загрузки (ссылка в конце статьи) и распаковать его в любую папку. После этого JRE уже готов к работе.
Однако запустить Java программу в этом случае можно только двумя способами:
- Из командной строки;
- С помощью программы загрузчика.
Первый вариант не подходит, так как слишком сложен для рядового пользователя. Остаётся второй.
Принцип работы загрузчика, на самом деле, достаточно прост. Программа передаёт JRE команды необходимые для запуска Java приложения и после этого закрывается. Тем самым автоматизируя процесс запуска, выполняемый в случае командной строки вручную.
Рассмотрим пример написания такого загрузчика на Delphi. Предположим, что загрузчик, jar файл с Java приложением и папка с JRE расположены в одной общей папке.
Определим константы (относительный путь к исполняемому файлу JRE и передаваемые команды (включают относительный путь к файлу Java приложения)).
Запуск приложений на Java/Scala без установленной JVM
Секрет, как вы уже могли догадаться, заключается в том, чтобы встроить JVM в само приложение. Получить такую «встраиваемую» JVM можно несколькими способами. Например, в этой заметке рассказывается, как собрать ее своими силами из JVM с сайта Oracle. А по этой ссылке доступна для скачивания готовая сборка, называется jPortable. Про последнюю вроде как везде очень хорошо отзываются, ею и воспользуемся.
@echo offbin\java.exe -jar swing-look-and-feel-switcher.jar
@echo on
Будучи упакованным в самораспаковывающийся 7-zip архив с максимальной степень сжатия все это хозяйство весит 32 Мб. Многовато, прямо скажем, но и не так, чтобы невероятно много. В наши дни пользователи и не столько качают. В крайнем случае вы можете попытаться выкинуть лишние файлы из каталогов bin и lib, но в этом случае нужно хорошо понимать, что вы делаете. Мне кажется, если вы пишите достаточно серьезное приложение, оно и само по себе будет весить немало. Пользователю нет особой разницы, качать 100 Мб или 132 Мб, особенно, если программа решает действительно актуальную для него задачу. Для сравнения, архив с IntelliJ IDEA 14.1 весит 183 Мб, и всем нормально.
В рамках поднятой темы нельзя также не отметить существование решений, позволяющих получать из Java-кода нативные приложения, вообще не требующие никакой JVM. В частности, есть такая интересная штука под названием Excelsior JET, но цена у нее довольно кусачая. Специально для разработки под iOS есть решение под названием RoboVM.
А вставала ли перед вами задача встраивать JVM в ваше приложение и если да, то каким образом вы ее решали? И кстати, не знает ли кто-нибудь, насколько лицензия JVM вообще позволяет такое использование виртуальной машины, в частности, в коммерческих проектах?
Дополнение: В комментариях подсказывают, что также есть официальный пакеджер от Oracle. Размер инсталлятора получается от 39 Мб. Также советуют попробовать бесплатный Excelsior Installer. А тут можно прочитать небольшой пост про RoboVM на русском языке.
Поскольку это так, мы рассмотрим, как вы можете запустить чистый Jar-файл Java-приложения в Windows.
Как я могу запустить файл Jar в Windows 10?
- Добавить Java в Windows
- Откройте Jar-файл с помощью бинарного файла Java (TM) Platform SE
- Запустите файл JAR из командной строки Windows
- Добавить сторонний Jar Executor в Windows
- Загрузите этот инструмент, мы настоятельно рекомендуем
1. Добавить Java в Windows
- Сначала откройте командную строку (Admin), нажав комбинацию клавиш Win + R и введя «cmd» в подсказке.
- Затем введите «java -version» в командной строке и нажмите Enter. Затем он предоставит дополнительную информацию о вашей версии Java, как показано ниже.
Подробнее о том, как добавить последнюю версию Java в Windows 10, читайте в этой обновленной статье.
2. Откройте файл Jar с двоичным файлом Java (TM) Platform SE
- В последней версии Java теперь вы можете запускать файлы Jar. Откройте проводник и папку с файлом, который вам нужен для запуска.
- Вы должны щелкнуть правой кнопкой мыши файл Jar и выбрать Открыть с помощью в контекстном меню.
- Нажмите Выберите другое приложение , а затем выберите, чтобы открыть его с помощью бинарного файла Java (TM) Platform SE .
Epic guide alert! Больше нет проблем с Проводником. Исправьте их все с помощью этого обширного руководства!
3. Запустите файл Jar из командной строки Windows
- Кроме того, вы можете запустить Jar из командной строки. Нажмите клавишу Win + горячую клавишу X и выберите Командная строка (Администратор) , чтобы открыть ее как администратор.
- Затем введите java j -jar c: pathtojarfile.jar ’в CP и нажмите Enter. Замените c: pathtojarfile.jar на фактический путь и заголовок файла Jar, который вам нужно запустить в Windows.
- Кроме того, вы можете сначала открыть папку с Jar в командной строке, введя «cd /», а затем каталог. Затем вы можете ввести «java -jar file.jar» без указания пути к папке.
Это откроет исполняемый файл Jar, если он содержит файл манифеста для указания точки входа приложения. (Разработчики должны были уже включить это в архив.)
Если вы разрабатываете свои собственные программы на Java, вам необходимо преобразовать Jar в исполняемый формат.
Если у вас возникли проблемы с доступом к командной строке от имени администратора, вам лучше ознакомиться с этим руководством.
4. Добавьте сторонний Jar Executor в Windows
Нажмите на файл jarx.exe, чтобы запустить программное обеспечение, которое на самом деле не имеет графического интерфейса пользователя (кроме окна About jarx). Затем вы можете дважды щелкнуть Jar-файлы, чтобы запустить их в Windows.
Если у вас возникли проблемы с доступом к файлу JAR или у вас возникла какая-то ошибка, взгляните на это изящное руководство, чтобы найти отличные исправления.
5. Загрузите этот инструмент, мы настоятельно рекомендуем
Новая обновленная версия имеет множество функций, которые сэкономят вам много времени при открытии, просмотре или редактировании файлов.Существует бесплатная полнофункциональная пробная версия, которую можно загрузить, так что вы можете проверить ее самостоятельно.
- Загрузить сейчас FileViewer Plus 3
Если у вас есть другие предложения или вопросы, оставьте их в разделе комментариев ниже, и мы обязательно их рассмотрим.
Про продукт
1С:Исполнитель – наш новый продукт, предназначенный в первую очередь для администрирования информационных систем на платформе 1С:Предприятие. Содержит кроссплатформенный язык сценариев (работает в Linux и Windows, в планах – поддержка macOS), библиотеку времени исполнения и среду разработки и отладки (можно работать в IDE на базе Eclipse и в Visual Studio Code).
Продукт написан на Java.
Под катом – рассказ о специфике самого продукта 1С:Исполнитель. Кому интересно именно про Graal – могут смело переходить к следующей секции.
Чуть подробнее о проекте, образ которого мы хотим в конечном счете получить. Исполнитель — это интерпретатор для кроссплатформенного языка сценариев, и сам язык сценариев к нему. Подобно тому, как cmd.exe исполняет bat-скрипты или bash исполняет bash-скрипты, Исполнитель работает со своими скриптами. Если вы, например, решили внедрить практику CI в разработку приложения на базе платформы 1С:Предприятие, то использование Исполнителя будет подходящим выбором, потому что Исполнитель содержит необходимый функционал для такой работы, а также позволяет избавить администратора от ограничений платформо-зависимых решений в пользу фокусировки на самой задаче. Кроме того, язык содержит объекты для работы с кластером серверов 1С и базами данных 1С:Предприятия, использование этих объектов может сильно облегчить администрирование платформы, еще в Исполнителе есть встроенные объекты для полноценной работы со системой взаимодействия. Посмотрим на примере, как Исполнитель может управлять кластером:
Запускать из консоли можно вот так:
Все эти сущности, которые отправляют email, читают xml и т.п. называются объектами языка (или просто объектами).
Стандартная поставка Исполнителя включает в себя скрипт с командами запуска (executor.cmd) и набор jar-ников, в которых содержатся используемые библиотеки, а также код описания и работы объектов:
Мы сами, в частности, активно используем 1С:Исполнитель для задач администрирования и автоматизации в наших высоконагруженных облачных сервисах 1cFresh и 1С:Готовое Рабочее Место (ГРМ).
Постановка задачи
В ходе использования 1С:Исполнителя и мы, и наши пользователи столкнулись с двумя проблемами:
- Недостаточно быстрый запуск продукта из-за инициализации Java на старте.
- В ряде компаний ИТ-политики не разрешают установку Java, что делает применение 1С:Исполнителя невозможным.
Про технологию
GraalVM Native Image — это технология, которая позволяет скомпилировать Java (и не только Java) приложение в нативный образ, то есть AOT компиляция из Java идет сразу в машинный код. Также можно компилировать в shared library или в статически связанный образ. Это может помочь сократить время запуска и уменьшить объем используемой памяти, так как не нужно будет держать мета информацию о классах. С технологией можно ознакомиться на сайте Грааля, подробный мануал. В этом же блоке затронем значимые вещи для использования в проектах.
Ограничения технологии
У Native Image есть ряд ограничений, перечислим основные.
Ограничения, которые можно обойти конфигурированием:
- Динамическая загрузка классов.
- Рефлексия.
- Динамические прокси.
Что работает по-другому
Основная вещь, которая работает по-другому в нативном образе в сравнении с привычным Java-миром — это, пожалуй, инициализация классов. В большинстве случаев инициализация классов происходит во время компиляции. Для нас данный факт, в частности, означает, что в статических переменных лучше не хранить значения, зависящие от конкретной машины или окружения (потому что с момента компиляции такие значение не поменяются). Кроме того, нельзя динамически подгружать библиотеки (из jar).
Native-Image Исполнителя
Давайте посмотрим, как мы создавали нативный образ Исполнителя (Исполнитель-Х). Если вы внимательно прочитали про ограничения выше и в мануале, то понимаете, что сходу собрать нативный образ большого приложения не получится. И у нас тоже была такая ситуация.
Вот как мы пошагово внедряли технологию в продукт.
В проекте нами используется система сборки Maven. Для того чтобы собрать приложение в нативный образ есть специальный плагин. Поэтому первым делом нужно подключить native-image-maven-plugin. Прошу обратить внимание, что в нем есть аргументы сборки, мы будем ими активно пользоваться. Ведь с их помощью можно конфигурировать процесс компиляции вашего образа и дальнейшие действия с ним.
Аргумент --no-fallback сообщает компилятору, что образ надо собирать такой, чтобы он работал без JVM (как раз то, что нам нужно). --allow-incomplete-classpath в свою очередь разрешает сборку даже если компилятор не может найти некоторые классы (включить их в образ). В нашем случае, если мы отключали эту опцию, то получали ошибку компиляции из-за попыток сослаться на классы, которые в 1С:Исполнителе даже не используются. Нужно помнить, что если во время сборки эти классы были недоступны, то и во время исполнения они доступными не будут, поэтому при попытках обратиться по их classpath будет выброшено исключение.
Так мы принялись впервые собирать и тестировать нативный образ Исполнителя. Однако же после того как мы получили образ, были обнаружены следующие проблемы:
- Нет объектов языка Исполнителя. Это те самые объекты, которые мы видели в разделе «Про 1С:Исполнитель» — объекты отправки почты, File и так далее. Все они лежат отдельно в jar-никах, мы их подгружаем в коде при старте Исполнителя в рантайме.
- Не работает часть функциональности, которая должна обеспечивать саму работу Исполнителя (даже без этих объектов). Например, интерфейс командной строки. Так, задание пути до скрипта с портом для дебага ($executor -d <port> -s <script_path>), или получение версии ($executor -v) не работает. Сами аргументы не разбираются по заданному правилу в одной из библиотек.
- Не отображаются тексты ошибок компиляции скрипта. Да и в целом тексты ошибок по всему проекту не отображаются.
Проблема в том, что в библиотеках, которые задействованы в нашем проекте, используется reflection, динамические прокси и динамическая загрузка классов. Значит, нам нужно создать конфигурационные файлы, которые будут участвовать при сборке и сообщать компилятору как и где используется, например, reflection. Для обработки нужно выписать classpath и флаги в такой файл в нужном формате. Но для этого нужно знать, где у нас этот reflection используется. Учесть все случаи использования в нашем случае вручную нереально. И вообще довольно трудно по всему проекту искать reflection, не говоря уже про библиотеки, код которых мы не контролируем. Тут на помощь приходит native-image-agent. Это специальная утилита к GraalVM, которая поможет нам найти reflection, динамический прокси и т.д. во всём проекте. Как это работает? Вы запускаете ваше Java-приложение вместе с аргументом agentlib:native-image-agent. Во время исполнения утилита выписывает в нужном формате reflection, proxy в конфигурационные файлы, которые уже потом будут использоваться при сборке нативного образа. То есть на этом шаге ваша задача определить сценарии работы приложения и прогнать их с агентом, потому что просто глядя на код GraalVM не сможет разобраться с ограничениями.
Поскольку необходимо именно исполнять приложение для отработки сценариев, в нашем случае мы написали код, который запускает Исполнитель вместе с выбранными скриптами и с разными аргументами. Эти скрипты заранее описаны, код для них взят из тестов на объекты, где мы стараемся отрабатывать крайние случаи и уж точно вызываем все методы (что, в конечном счете, для этих прогонов и нужно). Эти танцы с бубном проводятся для того, чтобы получившийся в итоге образ работал правильно.
- java
- IMessageList.java
- IMessageList_en.propeties
property файл тогда должен выглядеть вот так:
Самые внимательные могут спросить, почему поиск идет по имени, ведь это не столь надежно, а лучше бы искать по аннотации. Да, можно, однако потребовалось бы больше времени на разбор всех файлов во всех jar. В общем, пока нам хватит первого приближения для решения этой задачи.
После этих действий мы получили относительно нормально работающий нативный образ Исполнителя.
Однако перед нами возникла следующая проблема: логи из Исполнителя пишутся прямо в консоль (даже уровня debug), такого быть не должно. Более того файлы для логов не создаются. То есть у нас проблемы в целом с логированием во всём проекте.
Почему может не работать логирование? Мы помним, что классы инициализируются, как правило, при построении нативного образа. А тем более при построении инициализируются статические поля классов. Для нативного образа статическое поле значит, что оно меняться во время использования не будет. Поэтому одна из возможных причин поломки логирования – это использование логгеров в статических полях классов. То есть мы открываем файлы в статическом коде и с этими файлами работаем.
Вообще иметь в статических полях классов машинозависимые значения не рекомендуется (потому что пользователь при использовании вашего приложения обнаружит, что с момента компиляции образа значения не изменились).
После всех проб мы решили вообще на время отключить логи в Исполнителе, а еще мы позволили себе инициализировать все классы для логирования в buildtime, что в теории даст нам еще больший прирост скорости запуска.
Тем временем, мы приближаемся к корректно работающему Исполнителю.Путем проб и ошибок было установлено, что в аргументы при сборке надо добавить следующее:
Добавили все кодировки и передали образу Java-аргумент на установку выбранной кодировки. Также прописать кодировку надо и при запуске самого образа. Если кодировки при сборке образа и при его запуске будет отличаться, то мы опять получим кракозябры.Однако, что мы получаем для нативного образа Исполнителя в Windows, что у нас кодировка вывода всегда будет одна и та же, 866, и эта кодировка жестко прибита в образе? К сожалению, да, здесь уже как-то побороть или придумать другое решение мы не смогли. Если Вы его знаете, пожалуйста, напишите в комментариях. Если что, про chcp 65001 (UTF-8 в windows консоли) мы в курсе, попробовав собрать образ, получили, что ввод из stdin, содержащий кириллицу, трансформируется в кракозябры.
Опять-таки после этого у нас получился нативный бинарник Исполнителя ещё ближе к тому, что задумывалось. Однако мы столкнулись с ещё одной проблемой, вернее, с особенностью технологии. GraalVM Native Image не поддерживает вообще получение каких-либо переменных из окружения. Значит, получить локаль просто так не получится.
Замечание: вообще, получить переменные можно, только если передать проперти аргументом в бинарник специальным образом.
Для выбора языка, кстати, в итоге ввели специальный параметр в CLI. Короче говоря, у GraalVM Native Image c локализацией какие-то временные трудности.Промежуточный итог и общий процесс сборки
Итак, мы получили относительно корректно работающий нативный образ Исполнителя. Посмотрим на общий процесс сборки:
Результаты
Тестирование быстродействия велось на таком оборудовании:
- Оперативная память — 16 Гб.
- Процессор — Intel Core i5-3550 CPU 3.30 GHz x 4.
- Операционная система — Windows 10.
- Диск — SSD Samsung evo 850 EMT03B6Q, 250GB.
Исполнение простейшего скрипта («Hello world») для нативного образа Исполнителя занимает в разы меньше времени: 0,3с для нативного и 1,9с для стандартного. Надо заметить, что нативный образ вызывался ранее, но и обычная поставка также была вызвана несколько раз до этого (т.е. JVM уже «прогрета»).
Рассмотрим скрипт посложнее; в этом разбираются большое количество JSON-ов и из них получаются объекты и наоборот (примерно 1000 строк), кроме того есть много сравнений строк. Первый запуск образа занимал 1,9 с, для стандартного же — 3с, последующие запуски нативного образа занимали 0.5 секунд, а в стандартном Исполнителе 2,8 с. Разница по ощущениям для пользователя довольно большая (особенно если работать в паттерне «поменял что-то — сразу запустил»).Еще на языке Исполнителя был реализован алгоритм решета Эратосфена (без оптимизаций и т.п., так как нам нужно сравнить Исполнители).
Ниже представлены результаты в зависимости от разных границ, до которой считаем простые:Для N = 10^7 видно, что нативный образ выигрывает (50с против 110с) у стандартной поставки. Однако для N = 10^8 время уже сравнимое (900c и 1100c) — значит, мы где-то близко к условной границе оптимальной применимости образа. Действительно, для N = 3 * 10^8 нативный образ исполняет скрипт с решетом за 4200с, когда обычный — за 3300 с.
Тут мы видим JIT-компиляцию во всей её красе. А еще то, что, SubstrateVM не рассчитан на работу с большим объемом памяти.Суммарный вес образа Исполнителя стал 100мб, что на самом деле мало, потому что мы должны получить классы из стандартной поставки Java, кроме того мы должны включить в этот образ SubstrateVM и код Исполнителя и библиотек объектов (в обычном Исполнителе 40 Мб). Это отличный результат для вещи, которая работает изолированно.
Таким образом, мы выполнили те задачи, которые перед собой ставили:
- Мы избавили пользователя от скачивания Java (Исполнитель запускается как обычное приложение для Windows/Linux).
- Мы уменьшили время запуска Исполнителя.
Планы
Библиотеки, которые работают в нативном образе Исполнителя
Далее перечислены библиотеки, которые в итоге заработали в нативном образе Исполнителя, с их версиями:
Интересные факты и примеры
Учитывая, что локаль в рантайме у нас не поменяется, не слишком рациональный код, не правда ли (ну хотя бы работает)? Что ж, перепишем!Получим примерно такой код. Тут мы храним нужный нам бандл в не статическом и не константном поле класса. В конструкторе же определяем нужный бандл.
Однако во втором коде значение currentBundle никогда не меняется с момента компиляции, оставаясь одним из выбранных вариантов бандлов, который использовался во время сборки образа.Читайте также: