Throttling CPU/Memory usage of a Thread in Java?

33,018

Solution 1

If I understand your problem, one way would be to adaptively sleep the threads, similarly as video playback is done in Java. If you know you want 50% core utilization, the your algorithm should sleep approximately 0.5 seconds - potentially distributed within a second (e.g. 0.25 sec computation, 0.25 sec sleep, e.t.c.). Here is an example from my video player.

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

This code will sleep based on the frames/second value.

To throttle the memory usage, you could wrap your object creation into a factory method, and use some kind of semaphore with a limited permits as bytes to limit the total estimated object size (you need to estimate the size of various objects to ration the semaphore).

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

Solution 2

Care of Java Forums. Basically timing your execution and then waiting when your taking too much time. As is mentioned in the original thread, running this in a separate thread and interrupting the work thread will give more accurate results, as will averaging values over time.

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
}

Solution 3

You can get a lot of info about CPU and memory usage via JMX, but I don't think it allows any active manipulation.

For controlling CPU usage to some degree, you can use Thread.setPriority().

As for memory, there is no such thing as per-thread memory. The very concept of Java threads means shared memory. The only way to control memory usage is via the command line options like -Xmx, but there's no way to manipulate the settings at runtime.

Solution 4

If you run the threads in a separate process you can cap the memory usage and limit the number of CPUs or change the priority of these threads.

However, anything you do is likely to add overhead and complexity which is often counter-productive.

Unless you can explain why you would want to do this (e.g. you have a badly written library you don't trust and can't get support for) I would suggest you don't need to.

The reason its not easy to restrict memory usage is there is only one heap which is shared. So an object which is used in one thread is usable in another and is not assigned to one thread or another.

Limiting CPU usage means stopping all the threads so they don't do anything, however a better approach is to make sure the thread don't waste CPU and are only active doing work which needs to be done, in which case you wouldn't want to stop them doing it.

Solution 5

Why not instead of doing "threading" do cooperative multitasking, would be interesting to see if you can manipulate http://www.janino.net/ to run a program for a certain amount of time/set of intstructions, then stop and run the next program. At least that way its fair, give everyone the same time slice...

Share:
33,018

Related videos on Youtube

Alex Beardsley
Author by

Alex Beardsley

Updated on December 06, 2020

Comments

  • Alex Beardsley
    Alex Beardsley over 3 years

    I'm writing an application that will have multiple threads running, and want to throttle the CPU/memory usage of those threads.

    There is a similar question for C++, but I want to try and avoid using C++ and JNI if possible. I realize this might not be possible using a higher level language, but I'm curious to see if anyone has any ideas.

    EDIT: Added a bounty; I'd like some really good, well thought out ideas on this.

    EDIT 2: The situation I need this for is executing other people's code on my server. Basically it is completely arbitrary code, with the only guarantee being that there will be a main method on the class file. Currently, multiple completely disparate classes, which are loaded in at runtime, are executing concurrently as separate threads.

    The way it's written, it would be a pain to refactor to create separate processes for each class that gets executed. If that's the only good way to limit memory usage via the VM arguments, then so be it. But I'd like to know if there's a way to do it with threads. Even as a separate process, I'd like to be able to somehow limit its CPU usage, since as I mentioned earlier, several of these will be executing at once. I don't want an infinite loop to hog up all the resources.

    EDIT 3: An easy way to approximate object size is with java's Instrumentation classes; specifically, the getObjectSize method. Note that there is some special setup needed to use this tool.

    • James McMahon
      James McMahon over 14 years
      What are you using for a threading model? The Java task executors?
    • James McMahon
      James McMahon over 14 years
      Also, where are your bottlenecks in this application? Database? IO?
    • Dan Davies Brackett
      Dan Davies Brackett over 14 years
      The only time I can think of where you'd want to cap CPU was if battery life was an issue (and your question then becomes, how do I discover when I'm doing computationally expensive things on a battery-limited device?). Otherwise, why make the user wait longer than they need to? If you want to maintain system responsiveness, use low thread priority rather than attempting to cap CPU usage.
    • Alex Beardsley
      Alex Beardsley over 14 years
      @nemo The bottleneck in this case is only the amount of memory and CPU power you can cram into the server running the application. As I mentioned above, a thread caught in an infinite loop will hog up tons of resources, leaving other threads at its mercy. It would be really hard to tell if something is in an infinite loop or if its legitimately processor intensive. Either way I don't want one or two threads eating up all the resources. I'd like this to be as parallelized as possible, so the smaller less intensive threads can finish quickly.
    • Alex Beardsley
      Alex Beardsley over 14 years
      @nemo It's using Thread objects. The constructor takes in the class that gets loaded in at runtime, and uses reflection to execute the main method on that class.
  • Alex Beardsley
    Alex Beardsley over 14 years
    I'd like to make this easier for people to look at quickly; would you mind adding a bit more detail as to what you're doing, and commenting the code?
  • akarnokd
    akarnokd over 14 years
    It won't help much as the sample code is already too long without comments. But I'll do it.
  • Alex Beardsley
    Alex Beardsley over 14 years
    Added the reasoning for this in the OP.
  • Alex Beardsley
    Alex Beardsley over 14 years
    This is a really clever way to handle memory management, and is the only good direction anyone's given so far. As is it won't work for me because of EDIT 2 in my OP, but I'll probably end up using Aspects to intercept new objects getting created for that thread, and keep track of it with a semaphore for each Thread.
  • Vishy
    Vishy over 14 years
    An infinite loop will consume only one core. Many new servers have 4 to 16 cores so this might not be such a problem as it used to be. Note: an independent process can be safely killed for any reason.
  • davenpcj
    davenpcj about 11 years
    System.nanoTime() will give you a nanosecond timestamp good for benchmarking and other high-resolution time events. It's not real time though, so you'd need to capture a reference timestamp from the previous frame/sleep.
  • Rob Hall
    Rob Hall over 10 years
    If you have a spare thread and require garbage collection (due to some constraint preventing you from being able to track the necessary lifetime of your objects), you can potentially use a ReferenceQueue and SoftReferences to prevent memory-lock-leak. Presently, if an object if orphaned and GCd, then you cannot reclaim that memory. If use you have a thread checking a reference queue, then you could release the semaphore and log the mistake.
  • Thomas Hunziker
    Thomas Hunziker over 6 years
    In practice this will not help because under Java normally all threads will be executed with the same priority. So the priority is normally ignored.