¿Limitando el uso de CPU / Memoria de un subproceso en Java?


Estoy escribiendo una aplicación que tendrá varios subprocesos en ejecución, y quiero limitar el uso de CPU/memoria de esos subprocesos.

Hay una pregunta similar para C++, pero quiero intentar evitar usar C++ y JNI si es posible. Me doy cuenta de que esto podría no ser posible usando un lenguaje de nivel superior, pero tengo curiosidad por ver si alguien tiene alguna idea.

EDITAR: Se ha añadido una recompensa; Me gustaría tener algunas ideas muy buenas y bien pensadas sobre esto.

EDITAR 2: La situación para la que necesito esto es ejecutar el código de otras personas en mi servidor. Básicamente es un código completamente arbitrario, con la única garantía de que habrá un método principal en el archivo de clase. Actualmente, varias clases completamente dispares, que se cargan en tiempo de ejecución, se ejecutan simultáneamente como subprocesos separados.

De la forma en que está escrito, sería una molestia refactorizar crear procesos separados para cada clase que se ejecuta. Si eso es lo único bueno manera de limitar el uso de memoria a través de los argumentos de la VM, entonces que así sea. Pero me gustaría saber si hay una manera de hacerlo con hilos. Incluso como un proceso separado, me gustaría poder limitar de alguna manera su uso de CPU, ya que como mencioné anteriormente, varios de estos se ejecutarán a la vez. No quiero un bucle infinito para acaparar todos los recursos.

EDIT 3: Una forma fácil de aproximar el tamaño del objeto es con las clases java Instrumentation; específicamente, el método getObjectSize. Tenga en cuenta que hay alguna configuración especial necesaria para utilizar esta herramienta.

Author: Community, 2009-07-29

9 answers

Si entiendo su problema, una forma sería dormir adaptativamente los hilos, de manera similar a como la reproducción de video se realiza en Java. Si sabe que desea una utilización del núcleo del 50%, el algoritmo your debería dormir aproximadamente 0,5 segundos, potencialmente distribuido en un segundo (por ejemplo, cálculo de 0,25 segundos, sueño de 0,25 segundos, etc.). Aquí hay un ejemplo de mi reproductor de vídeo.

long starttime = 0; // variable declared
//...
// for the first time, remember the timestamp
if (frameCount == 0) {
    starttime = System.currentTimeMillis();
}
// the next timestamp we want to wake up
starttime += (1000.0 / fps);
// Wait until the desired next time arrives using nanosecond
// accuracy timer (wait(time) isn't accurate enough on most platforms) 
LockSupport.parkNanos((long)(Math.max(0, 
    starttime - System.currentTimeMillis()) * 1000000));

Este código dormirá basado en el valor frames/second.

Para limitar el uso de memoria, usted podría envolver su creación de objetos en un método de fábrica, y usar algún tipo de semáforo con permisos limitados como bytes para limitar el tamaño total estimado del objeto (necesita estimar el tamaño de varios objetos para racionar el semáforo).

package concur;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

public class MemoryLimited {
    private static Semaphore semaphore = new Semaphore(1024 * 1024, true);
    // acquire method to get a size length array
    public static byte[] createArray(int size) throws InterruptedException {
        // ask the semaphore for the amount of memory
        semaphore.acquire(size);
        // if we get here we got the requested memory reserved
        return new byte[size];
    }
    public static void releaseArray(byte[] array) {
        // we don't need the memory of array, release
        semaphore.release(array.length);
    }
    // allocation size, if N > 1M then there will be mutual exclusion
    static final int N = 600000;
    // the test program
    public static void main(String[] args) {
        // create 2 threaded executor for the demonstration
        ExecutorService exec = Executors.newFixedThreadPool(2);
        // what we want to run for allocation testion
        Runnable run = new Runnable() {
            @Override
            public void run() {
                Random rnd = new Random();
                // do it 10 times to be sure we get the desired effect
                for (int i = 0; i < 10; i++) {
                    try {
                        // sleep randomly to achieve thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // ask for N bytes of memory
                        byte[] array = createArray(N);
                        // print current memory occupation log
                        System.out.printf("%s %d: %s (%d)%n",
                            Thread.currentThread().getName(),
                            System.currentTimeMillis(), array,
                            semaphore.availablePermits());
                        // wait some more for the next thread interleaving
                        TimeUnit.MILLISECONDS.sleep(rnd.nextInt(100) * 10);
                        // release memory, no longer needed
                        releaseArray(array);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        // run first task
        exec.submit(run);
        // run second task
        exec.submit(run);
        // let the executor exit when it has finished processing the runnables
        exec.shutdown();
    }
}
 31
Author: akarnokd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-05 19:21:54

Puede obtener mucha información sobre el uso de CPU y memoria a través de JMX, pero no creo que permita ninguna manipulación activa.

Para controlar el uso de la CPU hasta cierto punto, puede usar Thread.setPriority () .

En cuanto a la memoria, no hay tal cosa como memoria por hilo. El propio concepto de Java threads significa memoria compartida. La única manera de controlar el uso de memoria es a través de las opciones de línea de comandos como-Xmx, pero no hay forma de manipular la configuración en tiempo de ejecución.

 5
Author: Michael Borgwardt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-29 18:46:55

Cuidado de Foros Java. Básicamente el tiempo de su ejecución y luego esperar cuando su toma demasiado tiempo. Como se menciona en el hilo original, ejecutar esto en un hilo separado e interrumpir el hilo de trabajo dará resultados más precisos, al igual que promediar los valores a lo largo del tiempo.

import java.lang.management.*;

ThreadMXBean TMB = ManagementFactory.getThreadMXBean();
long time = new Date().getTime() * 1000000;
long cput = 0;
double cpuperc = -1;

while(true){

if( TMB.isThreadCpuTimeSupported() ){
    if(new Date().getTime() * 1000000 - time > 1000000000){ //Reset once per second
        time = new Date().getTime() * 1000000;
        cput = TMB.getCurrentThreadCpuTime();
    }

    if(!TMB.isThreadCpuTimeEnabled()){
        TMB.setThreadCpuTimeEnabled(true);
    }

    if(new Date().getTime() * 1000000 - time != 0)
        cpuperc = (TMB.getCurrentThreadCpuTime() - cput) / (new Date().getTime() *  1000000.0 - time) * 100.0;                  
    }
//If cpu usage is greater then 50%
if(cpuperc > 50.0){
     //sleep for a little bit.
     continue;
}
//Do cpu intensive stuff
}
 5
Author: Andrew,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-05 20:28:40

Puede asignar diferentes prioridades a los subprocesos para que el subproceso más relevante se programe con más frecuencia.

Mira esta respuesta para ver si eso ayuda.

Cuando todos los subprocesos en ejecución tienen la misma prioridad, pueden ejecutarse así:

t1, t2, t3,     t1, t2, t3,   t1, t2, t3

Cuando se asigna una prioridad diferente a uno de ellos puede parecer:

t1, t1, t1, t1,    t2,    t1, t1, t1 t3.

Es decir, el primer hilo se ejecuta "más a menudo" que el resto.

 1
Author: OscarRyz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:54:47

Si ejecuta los subprocesos en un proceso separado, puede limitar el uso de memoria y limitar el número de CPU o cambiar la prioridad de estos subprocesos.

Sin embargo, cualquier cosa que hagas es probable que agregue sobrecarga y complejidad que a menudo es contraproducente.

A menos que pueda explicar por qué querría hacer esto (por ejemplo, tiene una biblioteca mal escrita en la que no confía y no puede obtener soporte), sugeriría que no es necesario.

La razón por la que no es fácil restringir la memoria el uso es que solo hay un montón que se comparte. Así que un objeto que se utiliza en un hilo es utilizable en otro y no se asigna a un hilo u otro.

Limitar el uso de la CPU significa detener todos los subprocesos para que no hagan nada, sin embargo, un mejor enfoque es asegurarse de que el subproceso no desperdicie la CPU y solo esté activo haciendo el trabajo que debe hacerse, en cuyo caso no querrá evitar que lo hagan.

 1
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-05 20:40:03

¿Por qué no en lugar de hacer "threading" hacer multitarea cooperativa, sería interesante ver si se puede manipular http://www.janino.net/ para ejecutar un programa durante una cierta cantidad de tiempo / conjunto de instrucciones, luego deténgase y ejecute el siguiente programa. Al menos de esa manera es justo, dar a todos la misma rebanada de tiempo...

 1
Author: JH.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-07 03:44:32

Hilo.setPriority() puede ayudar, pero no te permite limitar la CPU utilizada por un subproceso. De hecho, no he oído hablar de ninguna biblioteca Java que hace esto.

Podría ser posible implementar tal instalación siempre que sus hilos estén preparados para cooperar. La clave es hacer que los subprocesos llamen periódicamente a un programador personalizado, y hacer que el programador monitoree el uso de la CPU del subproceso usando JMX. Pero el problema es que si algún hilo no hace que el programador llame con la suficiente frecuencia, puede exceder los límites de estrangulamiento. Y no hay nada que puedas hacer sobre un hilo que se atasca en un bucle.

Otra ruta teórica para la implementación sería utilizar aislamientos. Desafortunadamente, será difícil encontrar una JVM de propósito general que implemente aislamientos. Además, las API estándar solo le permiten controlar el aislamiento, no los subprocesos dentro del aislamiento.

 0
Author: Stephen C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-30 03:12:41

La única forma de limitar el uso de la CPU del subproceso es mediante un bloque en un recurso o llamando a yield() con frecuencia.

Esto no limita el uso de CPU por debajo del 100%, pero da a otros subprocesos y procesos más intervalos de tiempo.

 0
Author: Thorbjørn Ravn Andersen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-07-30 08:21:28

Para reducir la CPU, desea dormir sus subprocesos dentro de los bucles comunes if y while.

while(whatever) {
    //do something
    //Note the capitol 'T' here, this sleeps the current thread.
    Thread.sleep(someNumberOfMilliSeconds);
}

Dormir unos pocos cientos de milisegundos reducirá en gran medida el uso de la CPU con poco o ningún resultado notable en el rendimiento.

En cuanto a la memoria, ejecutaría un generador de perfiles en los hilos individuales y haría algunos ajustes de rendimiento. Si limitaste la cantidad de memoria disponible para el hilo, creo que es probable una excepción de falta de memoria o un hilo muerto de hambre. Yo confiaría la JVM para proporcionar tanta memoria como el hilo necesario y trabajar en la reducción del uso de memoria manteniendo solo objetos esenciales en el alcance en un momento dado.

 -1
Author: James McMahon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-05 19:48:12