В некоторых случаях вы хотели бы запустить процесс Java, идентичный текущему (или, по крайней мере, использовать ту же JVM с измененными параметрами). Некоторые конкретные случаи, когда это было бы полезно:
- Автоматическая настройка параметров максимальной памяти (т. Е. У вас есть алгоритм для определения оптимального значения — например, 80% системной памяти — и ваша JVM не была запущена с этим конкретным значением)
- Создание кластера процессов для высокой (er) -доступности (истинная HA подразумевает наличие нескольких физических узлов) или потому, что процессы играют разные роли (например, компоненты в MongoDB).
- Демонизация текущего процесса (то есть фоновый процесс должен запускаться даже после завершения процесса запуска) — это очень часто modus-operandi для программ в системах * nix, где у вас есть процесс «управления» переднего плана и фоновый «демон» msgstr «процесс (не путать с потоками» демона «).
Это относительно просто — и может быть сделано в чистой Java — после того, как вы найдете правильные вызовы API:
List arguments = new ArrayList<>(); // the java executable arguments .add(String.format("%s%sbin%sjava", System.getProperty("java.home"), File.separator, File.separator)); // pre-execuable arguments (like -D, -agent, etc) arguments.addAll(ManagementFactory.getRuntimeMXBean() .getInputArguments()); String classPath = System.getProperty("java.class.path"), javaExecutable = System .getProperty("sun.java.command"); if (classPath.equals(javaExecutable)) { // was started with -jar arguments.add("-jar"); arguments.add(javaExecutable); } else { arguments.add("-classpath"); arguments.add(classPath); arguments.add(javaExecutable); } // we might add additional arguments here which will be received by the // launched program // in its args[] paramater arguments.add("runme"); // launch it! new ProcessBuilder().command(arguments).start();
Некоторые пояснения по поводу кода:
- Это во многом вдохновлено этим проектом
- Мы предполагаем, что исполняемый файл Java имеет имя
java
и находитсяbin/java
относительноjava.home
. Мы используемFile.separator
для кода быть переносимым. - getInputArguments используется для получения конкретных аргументов, передаваемых в JVM (например
-Xmx
). Он не включает путь к классам. - Который взят из
java.class.path
- Наконец, есть один эвристический шаг: мы пытаемся определить, был ли мы запущены с использованием
-jar myjar.jar
синтаксиса илиMyMainClass
синтаксиса, и повторить его.
Это оно! После этого мы используем ProcessBuilder (который мы всегда должны отдавать предпочтению Runtime.exec, потому что он автоматически экранирует части командной строки для нас).
Заключительная мысль: если вы намереваетесь использовать этот метод для «демонизации» процесса (т. Е. Чтобы он продолжал работать после завершения родительского процесса), вам следует сделать две вещи:
- Перенаправить стандартный ввод и вывод. По умолчанию они перенаправляются во временные буферы, и JVM, по-видимому, случайным образом завершает работу, когда эти буферы (каналы) заполняются.
- Под Windows используйте
javaw
вместоjava
. Это гарантирует, что процесс не будет привязан к консоли, с которой он был запущен (однако он все равно будет привязан к сеансу входа пользователя в систему и завершится, когда пользователь выйдет из системы — для более тяжелого решения загляните в сервис Java). Обертка ).
Это все на сегодня, надеюсь, вам понравилось, понравилось. Если вы запускаете код, и он не работает так, как было объявлено, дайте мне знать, чтобы я мог обновить его (особенно мне интересно, работает ли он с JVM не от Sun / Oracle). Приходите завтра за другой статьей!
Мета: этот пост является частью Java Advent Calendar и лицензирован под лицензией Creative Commons 3.0 Attribution . Если вам это нравится, пожалуйста, распространите информацию, поделившись, чирикать, FB, G + и так далее! Хотите написать для блога? Мы ищем участников для заполнения всех 24 слотов и хотели бы получить ваш вклад! Свяжитесь с Attila Balazs, чтобы внести свой вклад!