34 votes

Alternatives Java Runtime.getRuntime().exec()

J'ai une collection de webapps qui fonctionnent sous tomcat. Tomcat est configuré pour avoir jusqu'à 2 Go de mémoire en utilisant l'argument -Xmx.

De nombreuses applications web doivent effectuer une tâche qui finit par faire appel au code suivant :

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

Le problème que nous rencontrons est lié à la manière dont ce "processus enfant" est créé sous Linux (Redhat 4.4 et Centos 5.4).

Je crois comprendre qu'une quantité de mémoire égale à celle que tomcat utilise doit être libre dans le pool de mémoire physique (non swap) du système pour que ce processus enfant puisse être créé. Lorsque nous n'avons pas assez de mémoire physique libre, nous obtenons ceci :

    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

Mes questions sont les suivantes :

1) Est-il possible de supprimer l'exigence selon laquelle une quantité de mémoire égale au processus parent doit être libre dans la mémoire physique ? Je cherche une réponse qui me permette de spécifier combien de mémoire le processus enfant obtient ou de permettre à java sous linux d'accéder à la mémoire swap.

2) Quelles sont les alternatives à Runtime.getRuntime().exec() si aucune solution au #1 n'existe ? Je n'en ai retenu que deux, dont aucune n'est très désirable. JNI (très peu souhaitable) ou réécrire le programme que nous appelons en Java et en faire un processus propre avec lequel la webapp communique d'une manière ou d'une autre. Il doit y en avoir d'autres.

3) Y a-t-il un autre aspect de ce problème que je ne vois pas et qui pourrait potentiellement le résoudre ? Diminuer la quantité de mémoire utilisée par tomcat n'est pas une option. L'augmentation de la mémoire sur le serveur est toujours une option, mais cela semble être plus un pansement.

Les serveurs utilisent Java 6.

EDIT : Je dois préciser que je ne cherche pas un correctif spécifique à Tomcat. Ce problème peut être observé avec n'importe laquelle des applications java que nous avons sur le serveur web (il y en a plusieurs). J'ai simplement utilisé tomcat comme exemple parce qu'il aura probablement le plus de mémoire allouée et c'est là que nous avons vu l'erreur la première fois. Il s'agit d'une erreur reproductible.

EDIT : Finalement, nous avons résolu ce problème en réécrivant ce que l'appel système faisait en java. Je pense que nous avons eu beaucoup de chance de pouvoir le faire sans faire d'appels système supplémentaires. Tous les processus ne seront pas en mesure de le faire, donc j'aimerais bien voir une solution réelle à ce problème.

6voto

luke Points 6688

J'ai trouvé une solution de contournement dans ce article En gros, l'idée est de créer un processus au début du démarrage de votre application avec lequel vous communiquez (via des flux d'entrée), puis ce sous-processus exécute vos commandes à votre place.

//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());
    }
}

alors vous feriez un programme java d'aide

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);
         }
    }
}

ce que nous avons essentiellement fait est de créer un petit serveur "exec" pour votre application. Si vous initialisez votre classe ProcessHelper au début de votre application, elle créera avec succès ce processus, puis vous pourrez simplement lui envoyer des commandes. Comme le second processus est beaucoup plus petit, il devrait toujours réussir.

Vous pourriez également rendre votre protocole un peu plus approfondi, par exemple en renvoyant des codes de sortie, en notifiant les erreurs, etc.

5voto

Ian McLaird Points 2487

Essayez d'utiliser un ProcessBuilder . Les docteurs disent que c'est la façon "préférée" de démarrer un sous-processus de nos jours. Vous devriez également envisager d'utiliser la carte d'environnement (les documents sont dans le lien) pour spécifier les allocations de mémoire pour le nouveau processus. Je soupçonne (mais je n'en suis pas certain) que la raison pour laquelle il a besoin de tant de mémoire est qu'il hérite des paramètres du processus tomcat. L'utilisation de la carte d'environnement devrait vous permettre de modifier ce comportement. Cependant, notez que le démarrage d'un processus est très spécifique au système d'exploitation, donc YMMV.

1voto

Kam Points 11

Je pense que cela pourrait aider. Je sais que c'est un vieux sujet, mais pour les futures références... http://wrapper.tanukisoftware.com/doc/english/child-exec.html

1voto

gregturn Points 989

Une autre solution consiste à faire tourner un processus exec distinct qui surveille un fichier ou un autre type de "file d'attente". Vous ajoutez la commande que vous souhaitez à ce fichier, et le serveur exec le repère, exécute la commande et écrit les résultats à un autre endroit que vous pouvez obtenir.

1voto

JoG Points 1174

La meilleure solution que j'ai trouvée jusqu'à présent est d'utiliser un shell sécurisé avec des clés publiques. En utilisant http://www.jcraft.com/jsch/ J'ai créé une connexion à localhost et exécuté les commandes comme ça. Il y a peut-être un peu plus de frais généraux, mais pour ma situation, cela a fonctionné à merveille.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X