Domanda

Sto scrivendo un'applicazione che avrà più thread in esecuzione e desidera limitare l'utilizzo della CPU / memoria di tali thread.

Esiste una una domanda simile per C ++ , ma voglio provare a evitare di usare C ++ e JNI se possibile. Mi rendo conto che ciò potrebbe non essere possibile utilizzando un linguaggio di livello superiore, ma sono curioso di vedere se qualcuno ha qualche idea.

MODIFICA: aggiunta una taglia; Vorrei alcune idee davvero valide e ben ponderate su questo.

MODIFICA 2: La situazione di cui ho bisogno è l'esecuzione del codice di altre persone sul mio server. Fondamentalmente è un codice completamente arbitrario, con l'unica garanzia che ci sarà un metodo principale nel file di classe. Attualmente, più classi completamente disparate, che vengono caricate in fase di esecuzione, vengono eseguite contemporaneamente come thread separati.

Il modo in cui è scritto, sarebbe un problema refactoring per creare processi separati per ogni classe che viene eseguita. Se questo è l'unico buon modo per limitare l'utilizzo della memoria tramite gli argomenti della VM, allora così sia. Ma vorrei sapere se c'è un modo per farlo con i thread. Anche come processo separato, vorrei essere in grado di limitare in qualche modo il suo utilizzo della CPU, poiché come ho già detto in precedenza, molti di questi verranno eseguiti contemporaneamente. Non voglio un loop infinito per racimolare tutte le risorse.

EDIT 3: Un modo semplice per approssimare la dimensione dell'oggetto è con java's Instrumentation classi; in particolare, il metodo getObjectSize. Tieni presente che sono necessarie alcune impostazioni speciali per utilizzare questo strumento.

È stato utile?

Soluzione

Se capisco il tuo problema, un modo sarebbe quello di mettere in modo adattivo i thread, allo stesso modo in cui la riproduzione video viene eseguita in Java. Se sai di voler utilizzare il 50% di core, l'algoritmo dovrebbe dormire circa 0,5 secondi, potenzialmente distribuito in un secondo (ad es. Calcolo di 0,25 secondi, sonno di 0,25 secondi, e.t.c.). Ecco un esempio dal mio lettore video.

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

Questo codice dormirà in base al valore di frame / secondo.

Per limitare l'utilizzo della memoria, è possibile racchiudere la creazione dell'oggetto in un metodo di fabbrica e utilizzare un tipo di semaforo con autorizzazioni limitate come byte per limitare la dimensione totale stimata dell'oggetto (è necessario stimare la dimensione di vari oggetti per razione il semaforo).

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

Altri suggerimenti

Puoi ottenere molte informazioni sull'uso della CPU e della memoria tramite JMX , ma non credo che permetta alcuna manipolazione attiva.

Per controllare in qualche modo l'utilizzo della CPU, è possibile utilizzare Thread.setPriority () .

Per quanto riguarda la memoria, non esiste una memoria per thread. Il concetto stesso di thread Java significa memoria condivisa. L'unico modo per controllare l'utilizzo della memoria è tramite le opzioni della riga di comando come -Xmx, ma non c'è modo di manipolare le impostazioni in fase di esecuzione.

Cura di Forum Java . Fondamentalmente il tempismo della tua esecuzione e quindi l'attesa quando stai impiegando troppo tempo. Come indicato nel thread originale, eseguirlo in un thread separato e interrompere il thread di lavoro fornirà risultati più accurati, così come la media dei valori nel tempo.

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
}

Puoi assegnare priorità diverse ai thread in modo che il thread più rilevante venga pianificato più spesso.

Guarda questo rispondi per vedere se questo aiuta.

Quando tutti i thread in esecuzione hanno la stessa priorità, possono essere eseguiti in questo modo:

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

Quando assegni una priorità diversa a una di esse, potrebbe apparire come:

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

Cioè, il primo thread esegue " più spesso " che il resto.

Se si eseguono i thread in un processo separato, è possibile limitare l'utilizzo della memoria e limitare il numero di CPU o modificare la priorità di questi thread.

Tuttavia, qualsiasi cosa tu faccia probabilmente aggiungerà sovraccarico e complessità che è spesso controproducente.

A meno che tu non possa spiegare perché vorresti farlo (ad es. hai una biblioteca scritta male di cui non ti fidi e di cui non puoi ottenere supporto) Suggerirei che non è necessario.

Il motivo per cui non è facile limitare l'utilizzo della memoria è che esiste un solo heap condiviso. Quindi un oggetto utilizzato in un thread è utilizzabile in un altro e non è assegnato a un thread o a un altro.

Limitare l'utilizzo della CPU significa arrestare tutti i thread in modo che non facciano nulla, tuttavia un approccio migliore è assicurarsi che il thread non sprechi CPU e siano attivi solo facendo il lavoro che deve essere fatto, nel qual caso tu non vorrei impedirgli di farlo.

Perché non invece di fare " threading " fare un multitasking cooperativo, sarebbe interessante vedere se riesci a manipolare http://www.janino.net/ per eseguire un programma per un determinato periodo di tempo / serie di istruzioni, quindi interrompere ed eseguire il programma successivo. Almeno in questo modo è giusto, dai a tutti la stessa fetta di tempo ...

Thread.setPriority () può aiutare, ma non ti consente di limitare la CPU utilizzata da un thread. In effetti, non ho sentito parlare di alcuna libreria Java che lo faccia.

Potrebbe essere possibile implementare una tale struttura a condizione che i tuoi thread siano pronti a collaborare. La chiave è fare in modo che i thread chiamino periodicamente in uno scheduler personalizzato e che lo scheduler controlli l'utilizzo della CPU del thread mediante JMX. Ma il problema è che se un thread non effettua abbastanza spesso la chiamata dello scheduler, potrebbe superare i limiti di limitazione. E non c'è niente che tu possa fare per un thread che si blocca in un ciclo.

Un'altra via teorica per l'implementazione sarebbe quella di utilizzare gli isolati. Sfortunatamente, sarà difficile trovare una JVM generica che implementa gli isolati. Inoltre, le API standard consentono solo di controllare l'isolato, non i thread all'interno dell'isolato.

L'unico modo per limitare l'utilizzo della CPU di Thread è tramite un blocco su una risorsa o chiamare frequentemente yield ().

Ciò non limita l'utilizzo della CPU al di sotto del 100%, ma fornisce ad altri thread ed elabora più tempi.

Per ridurre la CPU, desideri inserire i thread all'interno dei comuni se e mentre cicli.

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

Dormire per qualche centinaio di millisecondi ridurrà notevolmente l'utilizzo della CPU con risultati quasi nulli sulle prestazioni.

Per quanto riguarda la memoria, eseguivo un profiler sui singoli thread e eseguivo un tuning delle prestazioni. Se hai limitato la quantità di memoria disponibile per il thread, penso che sia probabile un'eccezione di memoria esaurita o un thread affamato. Mi fiderei che JVM fornisse tutta la memoria necessaria per il thread e lavorasse per ridurre l'utilizzo della memoria mantenendo solo gli oggetti essenziali nell'ambito in qualsiasi momento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top