В некоторых случаях вы хотели бы запустить процесс 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, чтобы внести свой вклад!