La limitation CPU/Mémoire d'utilisation d'un Thread en Java?

j'écris une application qui aura plusieurs threads en cours d'exécution, et je veux étouffer L'utilisation CPU/Mémoire de ces threads.

il y a une question similaire pour C++ , mais je veux essayer d'éviter D'utiliser C++ et JNI si possible. Je me rends compte que ce n'est peut-être pas possible d'utiliser un langage de niveau supérieur, mais je suis curieux de voir si quelqu'un a des idées.

EDIT: , a Ajouté une prime; j'aimerais avoir quelques vraiment bon, bien pensé des idées sur ce.

EDIT 2: La situation dont j'ai besoin c'est de l'exécution d'autres personnes de code sur mon serveur. Fondamentalement, il s'agit de code complètement arbitraire, avec la seule garantie étant qu'il y aura une méthode principale sur le fichier de classe. Actuellement, plusieurs classes complètement disparates, qui sont chargées à l'exécution, s'exécutent simultanément sous forme de threads séparés.

la façon dont il est écrit, il serait une douleur à refactoriser à créer des processus distincts pour chaque classe qui est exécutée. Si c'est la seule bonne façon de limiter l'utilisation de la mémoire via les arguments VM, qu'il en soit ainsi. Mais j'aimerais savoir s'il y a un moyen de le faire avec des fringues. Même en tant que processus séparé, j'aimerais être capable de limiter d'une manière ou d'une autre son utilisation CPU, puisque comme je l'ai mentionné plus tôt, plusieurs d'entre eux seront exécutés en même temps. Je ne veux pas d'une boucle infinie pour monopoliser toutes les ressources.

EDIT 3: un moyen facile de se rapprocher de la taille de l'objet est de java Instrumentation classes; plus précisément, la méthode getObjectSize. Notez qu'il y a une configuration spéciale nécessaire pour utiliser cet outil.

39
demandé sur Community 2009-07-29 22:33:33

9 réponses

si je comprends votre problème, une façon serait de dormir adaptativement les threads, de la même manière que la lecture vidéo est faite en Java. Si vous savez que vous voulez une utilisation du cœur à 50%, votre algorithme devrait dormir environ 0,5 seconde - potentiellement répartie en moins d'une seconde (par exemple 0,25 seconde de calcul, 0,25 seconde de sommeil, E. T. C.). Voici un exemple de mon lecteur vidéo.

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

ce code dormira basé sur la valeur frames/seconde.

pour réduire l'utilisation de la mémoire, vous pouvez envelopper votre création d'objet dans une méthode d'usine, et utiliser une sorte de sémaphore avec des permis limités comme bytes pour limiter la taille totale estimée de l'objet (vous devez estimer la taille de divers objets pour rationner le sémaphore).

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
répondu akarnokd 2009-08-05 19:21:54

vous pouvez obtenir beaucoup d'informations sur L'utilisation du CPU et de la mémoire via JMX , mais je ne pense pas que cela permette une manipulation active.

pour contrôler L'utilisation du CPU dans une certaine mesure, vous pouvez utiliser Thread.setPriority () .

quant à la mémoire, il n'y a pas de mémoire par fil. Le concept même de Java threads signifie Mémoire partagée. La seule façon de contrôler l'utilisation de la mémoire est via les options de ligne de commande comme -Xmx, mais il n'y a aucun moyen de manipuler les paramètres à l'exécution.

5
répondu Michael Borgwardt 2009-07-29 18:46:55

Soins "de 151920920" Java Forums . Essentiellement chronométrer votre exécution et attendre quand vous prenez trop de temps. Comme indiqué dans le thread original, l'exécution de ce thread dans un thread séparé et l'interruption du thread de travail donneront des résultats plus précis, tout comme la moyenne des valeurs dans le temps.

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
répondu Andrew 2009-08-05 20:28:40

vous pouvez assigner des priorités différentes aux threads de sorte que le thread le plus pertinent soit programmé plus souvent.

regardez ça répondez pour voir si ça aide.

quand tous les thread ont la même priorité, ils peuvent tourner comme ceci:

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

Lorsque vous affectez une priorité différente à l'un d'eux, il peut ressembler à:

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

C'est-à-dire, le premier fil il court "plus souvent" que les autres.

1
répondu OscarRyz 2017-05-23 11:54:47

si vous exécutez les threads dans un processus séparé, vous pouvez capter l'utilisation de la mémoire et limiter le nombre de CPU ou changer la priorité de ces threads.

cependant, tout ce que vous faites est susceptible d'ajouter les frais généraux et la complexité qui est souvent contre-productif.

sauf si vous pouvez expliquer pourquoi vous voulez faire cela (par exemple, vous avez une bibliothèque mal écrite en laquelle vous n'avez pas confiance et pour laquelle vous ne pouvez pas obtenir de soutien), je suggérerais que vous n'avez pas besoin de le faire.

la raison pour laquelle il n'est pas facile de restreindre l'utilisation de la mémoire est qu'il n'y a qu'un tas qui est partagé. Donc un objet qui est utilisé dans un thread est utilisable dans un autre et n'est pas affecté à un fil ou d'une autre.

limiter L'utilisation du CPU signifie arrêter tous les threads afin qu'ils ne fassent rien, mais une meilleure approche est de s'assurer que le thread ne gaspille pas le CPU et ne sont actifs que pour faire le travail qui doit être fait, auquel cas vous ne voudriez pas les arrêter de le faire.

1
répondu Peter Lawrey 2009-08-05 20:40:03

pourquoi pas au lieu de faire "threading" faire multitâche coopérative, serait intéressant de voir si vous pouvez manipuler http://www.janino.net / pour exécuter un programme pendant un certain temps/ensemble d'instructions, puis arrêter et exécuter le programme suivant. Au moins c'est juste, donner à chacun la même tranche de temps...

1
répondu JH. 2009-08-07 03:44:32

Thread.setPriority () peut aider, mais il ne vous permet pas de capter le CPU utilisé par un thread. En fait, je n'ai jamais entendu parler d'une bibliothèque Java qui fait ça.

il pourrait être possible de mettre en œuvre une telle facilité à condition que vos fils soient prêts à coopérer. La clé est que les threads soient périodiquement appelés dans un planificateur personnalisé, et que le planificateur surveille l'utilisation du CPU des threads en utilisant JMX. Mais le problème est que si un fil ne fait pas le l'appel de l'ordonnanceur assez souvent il peut très bien dépasser les limites d'étranglement. Et il n'y a rien que vous puissiez faire à propos d'un fil qui est coincé dans une boucle.

une autre méthode théorique de mise en oeuvre consisterait à utiliser des isolats. Malheureusement, il vous sera difficile de trouver un JVM polyvalent qui implémente des isolats. De plus, les API standard ne permettent de contrôler que l'isolat, pas les threads à l'intérieur de l'isolat.

0
répondu Stephen C 2009-07-30 03:12:41

la seule façon de limiter l'utilisation CPU du Thread est de bloquer une ressource ou d'appeler yield() fréquemment.

cela ne limite pas L'utilisation CPU en dessous de 100% mais donne d'autres threads et traite plus de timeslices.

0
répondu Thorbjørn Ravn Andersen 2009-07-30 08:21:28

pour réduire CPU, vous voulez dormir vos fils à l'intérieur de commun si et tandis que boucles.

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

dormir pendant quelques centaines de millisecondes réduira considérablement l'utilisation de CPU avec peu ou pas de résultat perceptible sur la performance.

quant à la mémoire, je lançais un profileur sur les fils individuels et je faisais quelques réglages de performance. Si vous étouffé la quantité de mémoire disponible pour thread Je pense qu'une exception hors de la mémoire ou fil affamé est probable. Je ferais confiance à la JVM pour fournir autant de mémoire que le thread nécessaire et travailler sur la réduction de l'utilisation de la mémoire en gardant seulement les objets essentiels dans la portée à tout moment donné.

-1
répondu James McMahon 2009-08-05 19:48:12