Java Runtime.getRuntime (). Exec () альтернативы - PullRequest
34 голосов
/ 20 мая 2010

У меня есть коллекция веб-приложений, которые работают под tomcat. Tomcat настроен на использование до 2 ГБ памяти с помощью аргумента -Xmx.

Многие из веб-приложений должны выполнить задачу, в результате которой используется следующий код:

Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(command);
process.waitFor();
...

Проблема, с которой мы сталкиваемся, связана с тем, как этот «дочерний процесс» создается в Linux (Redhat 4.4 и Centos 5.4).

Насколько я понимаю, объем памяти, равный объему, который использует Tomcat, должен быть изначально свободен в пуле физической (не подкачки) системной памяти для создания этого дочернего процесса. Когда нам не хватает свободной физической памяти, мы получаем это:

    java.io.IOException: error=12, Cannot allocate memory
     at java.lang.UNIXProcess.<init>(UNIXProcess.java:148)
     at java.lang.ProcessImpl.start(ProcessImpl.java:65)
     at java.lang.ProcessBuilder.start(ProcessBuilder.java:452)
     ... 28 more

Мои вопросы:

1) Можно ли убрать требование об объеме памяти, равном свободному родительскому процессу в физической памяти? Я ищу ответ, позволяющий указать, сколько памяти дочерний процесс получает или разрешает Java на Linux получить доступ к памяти подкачки.

2) Каковы альтернативы Runtime.getRuntime (). Exec (), если не существует решения для # 1? Я мог бы думать только о двух, ни один из которых не очень желателен. JNI (очень нежелательно) или переписывание программы, которую мы вызываем на Java, и превращение ее в собственный процесс, с которым веб-приложение каким-то образом общается. Там должны быть другие.

3) Есть ли другая сторона этой проблемы, которую я не вижу, которая потенциально может это исправить? Уменьшение объема памяти, используемой tomcat, не вариант. Увеличение памяти на сервере - это всегда вариант, но похоже, что это просто пластырь.

Серверы работают на Java 6.

РЕДАКТИРОВАТЬ: я должен указать, что я не ищу конкретного кота исправить. Эта проблема может быть замечена с любым из Java-приложений, которые мы запускаем на веб-сервере (их несколько). Я просто использовал tomcat в качестве примера, потому что ему, скорее всего, будет выделено наибольшее количество памяти, и именно там мы впервые увидели ошибку. Это воспроизводимая ошибка.

EDIT: В конце концов, мы решили эту проблему, переписав то, что системный вызов делал в Java. Я чувствую, что нам очень повезло, что мы смогли сделать это без дополнительных системных вызовов. Не все процессы смогут сделать это, поэтому я все равно хотел бы увидеть фактическое решение этого вопроса.

Ответы [ 6 ]

6 голосов
/ 21 мая 2010

Я нашел обходной путь в этой статье , в основном идея заключается в том, что вы создаете процесс на раннем этапе запуска приложения, с которым вы общаетесь (через входные потоки), а затем этот подпроцесс выполняет ваши команды для тебя.

//you would probably want to make this a singleton
public class ProcessHelper
{
    private OutputStreamWriter output;
    public ProcessHelper()
    {
        Runtime runtime = Runtime.getRuntime();
        Process process = runtime.exec("java ProcessHelper");
        output = new OutputStreamWriter(process.getOutputStream());
    }
    public void exec(String command)
    {
        output.write(command, 0, command.length());
    }
}

тогда вы создадите вспомогательную Java-программу

public class ProcessHelper
{
    public static void main(String[] args)
    {
         BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
         String command;
         while((command = in.readLine()) != null)
         {
             Runtime runtime = Runtime.getRuntime();
             Process process = runtime.exec(command);
         }
    }
}

что мы, по сути, сделали, это сделали небольшой exec-сервер для вашего приложения. Если вы инициализируете свой класс ProcessHelper на раннем этапе в вашем приложении, он успешно создаст этот процесс, тогда вы просто передадите ему команды, поскольку второй процесс намного меньше, он всегда будет успешным.

Вы также можете сделать свой протокол немного более глубоким, например, возвращая коды выхода, уведомляя об ошибках и так далее.

5 голосов
/ 21 мая 2010

Попробуйте использовать ProcessBuilder . Документы говорят, что это «предпочтительный» способ запуска подпроцесса в наши дни. Вам также следует рассмотреть возможность использования карты среды (документы находятся в ссылке), чтобы указать допуски памяти для нового процесса. Я подозреваю (но не знаю наверняка), что причина, по которой ему нужно так много памяти, заключается в том, что он наследует настройки от процесса Tomcat. Использование карты среды должно позволить вам переопределить это поведение. Однако обратите внимание, что запуск процесса очень зависит от ОС, поэтому YMMV.

2 голосов
/ 21 мая 2010

I думаю это проблема unix fork (), требования к памяти возникают из-за того, как работает fork () - сначала она клонирует образ дочернего процесса (любого размера, в котором он находится в данный момент), а затем заменяет родительское изображение с дочерним изображением. Я знаю, что в Solaris есть какой-то способ контролировать это поведение, но я не знаю, что это такое.

Обновление : Это уже объяснено в Из какой версии ядра Linux / libc Java Runtime.exec () безопасна в отношении памяти?

1 голос
/ 23 августа 2013

Лучшее решение, которое я нашел, - это использование защищенной оболочки с открытыми ключами. Используя http://www.jcraft.com/jsch/, я создал соединение с localhost и выполнил подобные команды. Может быть, это немного больше накладных расходов, но для моей ситуации это сработало как шарм.

1 голос
/ 17 августа 2012

Другой альтернативой является запуск отдельного exec-процесса, который просматривает файл или какой-либо другой тип «очереди». Вы добавляете желаемую команду в этот файл, и сервер exec определяет ее, запускает команду и каким-то образом записывает результаты в другое место, которое вы можете получить.

1 голос
/ 12 сентября 2011

Думаю, это поможет. Я знаю, что это старая тема, только для будущих ссылок ... http://wrapper.tanukisoftware.com/doc/english/child-exec.html

...