java tomcat memory-management runtime.exec

Java Runtime.getRuntime(). Exec() alternatives



tomcat memory-management (6)

Creo que esto es un problema de unix fork (), el requisito de memoria proviene de they way fork () funciona: primero clona la imagen del proceso secundario (al tamaño que tenga actualmente) y luego reemplaza la imagen principal con la imagen secundaria. Sé en Solaris que hay alguna manera de controlar este comportamiento, pero no sé de qué se trata.

Actualización : Esto ya se explica en ¿De qué versión de Linux kernel / libc es Java Runtime.exec () seguro con respecto a la memoria?

Tengo una colección de aplicaciones web que se ejecutan bajo Tomcat. Tomcat está configurado para tener hasta 2 GB de memoria utilizando el argumento -Xmx.

Muchos de los webapps necesitan realizar una tarea que termine usando el siguiente código:

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

El problema que estamos teniendo está relacionado con la forma en que se está creando este "proceso hijo" en Linux (Redhat 4.4 y Centos 5.4).

Tengo entendido que una cantidad de memoria igual a la cantidad que usa tomcat debe estar libre en el conjunto de memoria física del sistema (sin intercambio) inicialmente para que se cree este proceso hijo. Cuando no tenemos suficiente memoria física libre, estamos obteniendo esto:

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

Mis preguntas son:

1) ¿Es posible eliminar el requisito de que una cantidad de memoria igual a la del proceso principal esté libre en la memoria física? Estoy buscando una respuesta que me permita especificar cuánta memoria recibe el proceso hijo o permitir que Java en Linux acceda a la memoria de intercambio.

2) ¿Cuáles son las alternativas a Runtime.getRuntime (). Exec () si no existe una solución al # 1? Solo pude pensar en dos, ninguno de los cuales es muy deseable. JNI (muy deseable) o reescribiendo el programa al que llamamos en Java y convirtiéndolo en su propio proceso con el que la aplicación web se comunica de alguna manera. Tiene que haber otros.

3) ¿Hay otro lado de este problema que no veo que podría solucionarlo? La reducción de la cantidad de memoria utilizada por tomcat no es una opción. Aumentar la memoria en el servidor siempre es una opción, pero parece ser más una curita.

Los servidores ejecutan Java 6.

EDITAR: Debería especificar que no estoy buscando una solución específica de tomcat. Este problema se puede ver con cualquiera de las aplicaciones Java que tenemos en ejecución en el servidor web (hay varias). Simplemente utilicé Tomcat como ejemplo porque probablemente tendrá la mayor cantidad de memoria asignada y es donde realmente vimos el error la primera vez. Es un error reproducible.

EDITAR: Al final, resolvimos este problema volviendo a escribir lo que estaba haciendo la llamada al sistema en Java. Siento que tuvimos mucha suerte de poder hacer esto sin hacer llamadas adicionales al sistema. No todos los procesos podrán hacerlo, por lo que aún me gustaría ver una solución real a esto.


Encontré una solución en este article , básicamente, la idea es que cree un proceso desde el principio de la aplicación con la que se comunica (a través de flujos de entrada) y luego ese subproceso ejecuta los comandos por usted.

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

entonces harías un programa java de ayuda

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

lo que esencialmente hemos hecho es crear un pequeño servidor ''ejecutivo'' para su aplicación. Si inicializa su clase ProcessHelper desde el principio en su aplicación, creará este proceso con éxito y luego simplemente transferirá los comandos al mismo, porque el segundo proceso es mucho más pequeño y siempre debe tener éxito.

También puede ampliar su protocolo un poco más, como devolver códigos de salida, notificar errores y más.



Intenta usar ProcessBuilder . Los doctores dicen que esa es la forma "preferida" de iniciar un subproceso en estos días. También debe considerar usar el mapa de entorno (los documentos están en el enlace) para especificar los permisos de memoria para el nuevo proceso. Sospecho (pero no lo sé con certeza) que la razón por la que necesita tanta memoria es que hereda la configuración del proceso de tomcat. Usar el mapa del entorno debería permitirle anular ese comportamiento. Sin embargo, tenga en cuenta que iniciar un proceso es muy específico del sistema operativo, por lo que YMMV.


La mejor solución que encontré hasta ahora fue el uso de shell seguro con claves públicas. Utilizando http://www.jcraft.com/jsch/ creé una conexión a localhost y ejecuté los comandos de esa manera. Tal vez tenga un poco más de sobrecarga, pero para mi situación funcionó a las mil maravillas.


Otra alternativa es ejecutar un proceso de ejecución independiente que mire un archivo u otro tipo de "cola". Usted agrega el comando deseado a ese archivo, y el servidor ejecutivo lo detecta, ejecuta el comando y, de alguna manera, escribe los resultados en otra ubicación que puede obtener.