在 Java 中限制线程的 CPU/内存使用率?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1202184/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:52:35  来源:igfitidea点击:

Throttling CPU/Memory usage of a Thread in Java?

javamemorymultithreadingcputhrottling

提问by Alex Beardsley

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

我正在编写一个将运行多个线程的应用程序,并希望限制这些线程的 CPU/内存使用。

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.

C++有一个类似的问题,但我想尽可能避免使用 C++ 和 JNI。我意识到使用高级语言可能无法做到这一点,但我很想知道是否有人有任何想法。

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.

编辑 2:我需要这个的情况是在我的服务器上执行其他人的代码。基本上它是完全任意的代码,唯一的保证是在类文件中会有一个 main 方法。目前,在运行时加载的多个完全不同的类作为单独的线程同时执行。

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.

按照它的编写方式,重构为每个执行的类创建单独的进程会很痛苦。如果这是通过 VM 参数限制内存使用的唯一好方法,那么就这样吧。但我想知道是否有办法用线程来做到这一点。即使作为一个单独的进程,我也希望能够以某种方式限制其 CPU 使用率,因为正如我之前提到的,其中几个将同时执行。我不希望无限循环占用所有资源。

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

编辑 3:一种近似对象大小的简单方法是使用 java 的Instrumentation类;具体来说,getObjectSize 方法。请注意,使用此工具需要进行一些特殊设置。

采纳答案by akarnokd

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 examplefrom my video player.

如果我理解您的问题,一种方法是自适应地使线程休眠,就像在 Java 中完成视频播放一样。如果你知道你想要 50% 的核心利用率,你的算法应该休眠大约 0.5 秒 - 可能在一秒内分布(例如 0.25 秒计算、0.25 秒休眠等)。这是我的视频播放器的示例

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

回答by Michael Borgwardt

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

您可以通过JMX获得大量有关 CPU 和内存使用情况的信息,但我认为它不允许任何主动操作。

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

为了在某​​种程度上控制 CPU 使用率,您可以使用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.

至于内存,没有每线程内存这样的东西。Java 线程的概念意味着共享内存。控制内存使用的唯一方法是通过命令行选项,如 -Xmx,但无法在运行时操作设置。

回答by OscarRyz

You can assign different priorities to the threads so the most relevant thread get scheduled more often.

您可以为线程分配不同的优先级,以便更频繁地调度最相关的线程。

Look at this answerto see if that helps.

看看这个答案,看看是否有帮助。

When all the running thread have the same priority they may run like this:

当所有正在运行的线程具有相同的优先级时,它们可能会像这样运行:

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

When you assign a different priority to one of them it may look like:

当您为其中一个分配不同的优先级时,它可能如下所示:

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

That is, the first thread runs "more often" that the rest.

也就是说,第一个线程比其余线程“更频繁地”运行。

回答by Stephen C

Thread.setPriority() may help, but it doesn't allow you to cap the CPU used by a thread. In fact, I've haven't heard of any Java library that does this.

Thread.setPriority() 可能会有所帮助,但它不允许您限制线程使用的 CPU。事实上,我还没有听说过任何这样做的 Java 库。

It might be possible to implement such a facility provided that your threads are prepared to cooperate. The key is to have the threads periodically call into a custom scheduler, and have the scheduler monitor thread CPU usage using JMX. But the problem is that if some thread doesn't make the scheduler call often enough it may well exceed the throttling limits. And there's nothing you can do about a thread that gets stuck in a loop.

如果您的线程准备好合作,则可以实现这样的功能。关键是让线程定期调用自定义调度程序,并让调度程序使用 JMX 监视线程 CPU 使用率。但问题是,如果某个线程没有足够频繁地调用调度程序,它很可能会超过节流限制。对于陷入循环中的线程,您无能为力。

Another theoretical route to implementing would be to use Isolates. Unfortunately, you will be hard pressed to find a general purpose JVM that implements isolates. Besides, the standard APIs only allow you to control the isolate, not the threads within the isolate.

另一种实现的理论途径是使用隔离。不幸的是,您将很难找到实现隔离的通用 JVM。此外,标准 API 只允许您控制隔离,而不是隔离内的线程。

回答by Thorbj?rn Ravn Andersen

The only way you can limit Thread CPU usage is by either block on a resource or to call yield() frequently.

限制线程 CPU 使用率的唯一方法是阻塞资源或频繁调用 yield()。

This does not limit CPU usage below 100% but gives other threads and processes more timeslices.

这不会将 CPU 使用率限制在 100% 以下,而是为其他线程和进程提供更多时间片。

回答by James McMahon

To reduce CPU, you want to sleep your threads inside of common ifand whileloops.

为了减少 CPU,您希望在常见的ifwhile循环中休眠您的线程。

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

Sleeping for a few hundred milliseconds will greatly reduce CPU usage with little to no noticeable result on performance.

休眠几百毫秒将大大降低 CPU 使用率,而对性能几乎没有明显影响。

As for the memory, I'd run a profiler on the individual threads and do some performance tuning. If you out throttled the amount of memory available to thread I think an out of memory exception or starved thread is likely. I would trust the JVM to provide as much memory as the thread needed and work on reducing the memory usage by keeping only essential objects in scope at any given time.

至于内存,我会在各个线程上运行分析器并进行一些性能调整。如果您限制了线程可用的内存量,我认为可能会出现内存不足异常或线程饥饿。我相信 JVM 会提供线程所需的尽可能多的内存,并通过在任何给定时间仅将必要对象保留在范围内来减少内存使用量。

回答by Andrew

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.

关心Java 论坛。基本上是定时执行,然后在花费太多时间时等待。正如在原始线程中提到的,在单独的线程中运行它并中断工作线程将提供更准确的结果,随着时间的推移求平均值也是如此。

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
}

回答by Peter Lawrey

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.

如果您在单独的进程中运行线程,您可以限制内存使用量并限制 CPU 数量或更改这些线程的优先级。

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.

限制 CPU 使用率意味着停止所有线程,以便它们不执行任何操作,但是更好的方法是确保线程不会浪费 CPU 并且只主动执行需要完成的工作,在这种情况下您不会想阻止他们这样做。

回答by JH.

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...

为什么不做“线程”而不是做协作多任务,看看你是否可以操纵http://www.janino.net/来运行一个程序一段时间/一组指令,然后停止并运行下一个节目。至少这样是公平的,给每个人相同的时间片......