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

发布于 2024-07-30 10:42:16 字数 804 浏览 3 评论 0原文

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

对于 C++ 有一个 类似的问题,但我想尝试避免使用 C++ 和 JNI,如果可能的。 我意识到使用高级语言可能无法实现这一点,但我很好奇是否有人有任何想法。

编辑:添加了赏金; 我想要一些非常好的、经过深思熟虑的想法。

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

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

窝囊感情。 2024-08-06 10:42:16

如果我理解你的问题,一种方法是自适应地休眠线程,类似于 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));

此代码将根据帧/秒值休眠。

为了限制内存使用,您可以将对象创建包装到工厂方法中,并使用某种具有有限许可的信号量作为字节来限制总估计对象大小(您需要估计各种对象的大小来分配信号量) )。

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

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();
    }
}
◇流星雨 2024-08-06 10:42:16

关注 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
}

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
}
天涯沦落人 2024-08-06 10:42:16

您可以通过 JMX,但我认为它不允许任何主动操作。

为了在一定程度上控制 CPU 使用率,可以使用 Thread.setPriority()

至于内存,不存在每线程内存之类的东西。 Java 线程的概念本身就是共享内存。 控制内存使用的唯一方法是通过 -Xmx 等命令行选项,但无法在运行时操作设置。

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.

随遇而安 2024-08-06 10:42:16

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

然而,您所做的任何事情都可能会增加开销和复杂性,这通常会适得其反。

除非你能解释为什么你想这样做(例如,你有一个写得很糟糕的库,你不信任并且无法获得支持),否则我建议你不需要这样做。

限制内存使用并不容易的原因是只有一个堆是共享的。 因此,在一个线程中使用的对象可以在另一个线程中使用,并且不会分配给一个线程或另一个线程。

限制CPU使用意味着停止所有线程,这样它们就不会做任何事情,但是更好的方法是确保线程不会浪费CPU并且只活跃地做需要完成的工作,在这种情况下你就不会想要阻止他们这样做。

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.

难理解 2024-08-06 10:42:16

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

看看这个 回答看看是否有帮助。

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

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

当您为其中一个线程分配不同的优先级时,它可能看起来像:

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

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

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

Look at this answer to 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.

眼角的笑意。 2024-08-06 10:42:16

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

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

乖乖 2024-08-06 10:42:16

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

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

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

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.

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.

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.

野生奥特曼 2024-08-06 10:42:16

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

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

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

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

愿得七秒忆 2024-08-06 10:42:16

为了减少 CPU 占用,您需要让线程在常见的 ifwhile 循环中休眠。

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

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

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

To reduce CPU, you want to sleep your threads inside of common if and while loops.

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.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文