it-swarm.com.de

CPU/Memory-Auslastung eines Threads in Java drosseln?

Ich schreibe eine Anwendung, in der mehrere Threads ausgeführt werden und die CPU-/Speicherauslastung dieser Threads drosseln soll.

Es gibt eine ähnliche Frage für C++ , aber ich möchte versuchen, C++ und JNI möglichst zu vermeiden. Mir ist klar, dass dies mit einer höheren Sprache möglicherweise nicht möglich ist, aber ich bin gespannt, ob jemand irgendwelche Ideen hat.

EDIT: Kopfgeld hinzugefügt; Ich hätte gerne einige wirklich gute, gut durchdachte Ideen dazu.

EDIT 2: Die Situation, für die ich dies benötige, besteht darin, den Code anderer Leute auf meinem Server auszuführen. Grundsätzlich handelt es sich um völlig beliebigen Code, mit der einzigen Garantie, dass die Klassendatei eine Hauptmethode enthält. Derzeit werden mehrere völlig unterschiedliche Klassen, die zur Laufzeit geladen werden, gleichzeitig als separate Threads ausgeführt.

Die Art und Weise, wie es geschrieben ist, wäre es ein Problem, für jede Klasse, die ausgeführt wird, separate Prozesse zu erstellen. Wenn dies die einzige gute Möglichkeit ist, die Speichernutzung über die Argumente VM zu begrenzen, ist dies auch möglich. Aber ich würde gerne wissen, ob es eine Möglichkeit gibt, dies mit Threads zu tun. Selbst als separater Prozess möchte ich in der Lage sein, die CPU-Auslastung irgendwie einzuschränken, da, wie bereits erwähnt, mehrere dieser Prozesse gleichzeitig ausgeführt werden. Ich möchte nicht, dass eine Endlosschleife alle Ressourcen verschlingt.

EDIT 3: Eine einfache Methode zur Annäherung der Objektgröße ist mit Javas Instrumentation Klassen; Insbesondere die Methode getObjectSize. Beachten Sie, dass für die Verwendung dieses Tools einige spezielle Einstellungen erforderlich sind.

39
Alex Beardsley

Wenn ich Ihr Problem verstehe, besteht eine Möglichkeit darin, die Threads adaptiv zu schlafen, ähnlich wie bei der Videowiedergabe in Java. Wenn Sie wissen, dass Sie eine Kernauslastung von 50% wünschen, sollte Ihr Algorithmus etwa 0,5 Sekunden lang schlafen - möglicherweise innerhalb einer Sekunde verteilt (z. B. 0,25-Sekunden-Berechnung, 0,25-Sekunden-Ruhezeit, e.t.c.). Hier ist ein Beispiel von meinem Videoplayer.

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

Dieser Code wird basierend auf dem Frame/Sekunde-Wert in den Ruhezustand versetzt.

Um die Speicherbelegung zu drosseln, können Sie Ihre Objekterstellung in eine Factory-Methode einbetten und eine Art Semaphor mit einer begrenzten Anzahl von Bytes verwenden, um die geschätzte Gesamtobjektgröße zu begrenzen (Sie müssen die Größe verschiedener Objekte schätzen, um das Semaphor zu bewerten ).

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
akarnokd

Pflege von Java-Foren . Grundsätzlich planen Sie Ihre Ausführung und warten dann, wenn Sie zu viel Zeit in Anspruch nehmen. Wie im ursprünglichen Thread erwähnt, führt das Ausführen in einem separaten Thread und das Unterbrechen des Arbeitsthreads zu genaueren Ergebnissen, da die Werte im Zeitverlauf gemittelt werden.

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
Andrew

Sie können über JMX viele Informationen zur CPU- und Speichernutzung erhalten, aber ich glaube nicht, dass dies eine aktive Manipulation erlaubt.

Um die CPU-Auslastung zu einem gewissen Grad zu steuern, können Sie Thread.setPriority () verwenden.

Was den Speicher angeht, so gibt es keinen Pro-Thread-Speicher. Das Konzept von Java-Threads bedeutet Shared Memory. Die Speicherbelegung kann nur über die Befehlszeilenoptionen wie -Xmx gesteuert werden. Die Einstellungen können jedoch zur Laufzeit nicht geändert werden.

5

Sie können den Threads unterschiedliche Prioritäten zuweisen, sodass der relevanteste Thread häufiger geplant wird. 

Sehen Sie sich diese Antwort an, um zu sehen, ob das hilft.

Wenn alle laufenden Threads die gleiche Priorität haben, können sie folgendermaßen ausgeführt werden:

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

Wenn Sie einem von ihnen eine andere Priorität zuweisen, kann dies wie folgt aussehen:

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

Das heißt, der erste Thread läuft "öfter" als der Rest. 

1
OscarRyz

Wenn Sie die Threads in einem separaten Prozess ausführen, können Sie die Speichernutzung begrenzen und die Anzahl der CPUs begrenzen oder die Priorität dieser Threads ändern.

Alles, was Sie tun, wird jedoch wahrscheinlich zusätzlichen Aufwand und Komplexität hinzufügen, was oft kontraproduktiv ist.

Wenn Sie nicht erklären können, warum Sie dies tun möchten (z. B. haben Sie eine schlecht geschriebene Bibliothek, der Sie nicht trauen und keinen Support erhalten), würde ich vorschlagen, dass Sie dies nicht tun müssen.

Der Grund, warum es nicht einfach ist, die Speichernutzung zu beschränken, ist, dass nur ein Heap gemeinsam genutzt wird. Ein Objekt, das in einem Thread verwendet wird, kann also in einem anderen Thread verwendet werden und ist keinem Thread zugeordnet.

Die Beschränkung der CPU-Nutzung bedeutet, dass alle Threads angehalten werden, damit sie nichts unternehmen. Es ist jedoch ein besserer Ansatz, sicherzustellen, dass der Thread keine CPU verschwendet und nur aktiv ist, um Arbeit auszuführen, die erledigt werden muss. In diesem Fall wäre dies nicht der Fall Ich möchte sie davon abhalten.

1
Peter Lawrey

Warum nicht kooperatives Multitasking durchführen, anstatt zu "fädeln", wäre interessant zu sehen, ob Sie http://www.janino.net/ manipulieren können, um ein Programm für eine bestimmte Zeit/Satz von Anweisungen auszuführen. dann stoppen und das nächste Programm ausführen. Zumindest so ist es fair, gib jedem die gleiche Zeitscheibe ...

1
JH.

Thread.setPriority () kann helfen, aber Sie können die von einem Thread verwendete CPU nicht begrenzen. In der Tat habe ich noch keine Java-Bibliothek gehört, die dies tut.

Eine solche Einrichtung kann möglicherweise implementiert werden, vorausgesetzt, Ihre Threads sind zur Zusammenarbeit bereit. Der Schlüssel besteht darin, dass die Threads regelmäßig einen benutzerdefinierten Scheduler aufrufen und der Scheduler die CPU-Nutzung von Threads mithilfe von JMX überwachen soll. Das Problem ist jedoch, dass, wenn ein Thread den Scheduler nicht oft genug anfordert, die Throttling-Grenzen möglicherweise überschritten werden. Und es gibt nichts, was Sie gegen einen Thread tun können, der in einer Schleife hängen bleibt.

Ein weiterer theoretischer Weg zur Implementierung wäre die Verwendung von Isolaten. Leider wird es Ihnen schwer fallen, eine allgemeine JVM zu finden, die Isolate implementiert. Außerdem können Sie mit den Standard-APIs nur das Isolat steuern, nicht die Threads innerhalb des Isolats. 

0
Stephen C

Die einzige Möglichkeit, die Thread-CPU-Nutzung einzuschränken, besteht darin, entweder eine Ressource zu blockieren oder das yield () häufig aufzurufen.

Dies beschränkt die CPU-Auslastung nicht unter 100%, sondern gibt anderen Threads und Prozessen mehr Zeitaufwand.