Java Runtime.getRuntime () .exec () альтернативы

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

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

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

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

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

    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) Там другая сторона к этой проблеме, что я не вижу, что это могло потенциально зафиксировать ее? Понижение объема памяти, используемого котом, не является опцией. Увеличение памяти на сервере всегда является опцией, но походит больше на лейкопластырь.

Серверы выполняют java 6.

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

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

34
задан twilbrand 25 August 2010 в 18:22
поделиться

3 ответа

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

5
ответ дан 27 November 2019 в 17:16
поделиться

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

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

2
ответ дан 27 November 2019 в 17:16
поделиться

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

//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 на ранней стадии своего приложения, он успешно создаст этот процесс, тогда вы просто передадите ему команды, потому что второй процесс намного меньше, он всегда должен быть успешным.

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

6
ответ дан 27 November 2019 в 17:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: