我正在编写一个运行多个线程的应用程序,并希望限制这些线程的CPU /内存使用量。
有similar question for C++,但我想尽可能避免使用C ++和JNI。我意识到使用更高级别的语言可能无法做到这一点,但我很想知道是否有人有任何想法。
编辑:增加了赏金;我想要一些非常好的,深思熟虑的想法。
编辑2:我需要这样做的情况是在我的服务器上执行其他人的代码。基本上它是完全任意的代码,唯一的保证是类文件上会有一个main方法。目前,在运行时加载的多个完全不同的类作为单独的线程同时执行。
编写它的方式,重构为每个执行的类创建单独的进程将是一件痛苦的事。如果这是通过VM参数限制内存使用的唯一好方法,那么就这样吧。但是我想知道是否有办法用线程来做。即使作为一个单独的进程,我也希望能够以某种方式限制其CPU使用率,因为正如我之前提到的,其中一些将立即执行。我不希望无限循环占用所有资源。
编辑3:近似对象大小的简单方法是使用java的Instrumentation类;特别是getObjectSize方法。请注意,使用此工具需要一些特殊设置。
答案 0 :(得分:32)
如果我理解你的问题,一种方法是自适应地睡眠线程,就像用Java完成视频播放一样。如果你知道你想要50%的核心利用率,你的算法应该睡眠大约0.5秒 - 可能在一秒钟内分布(例如0.25秒计算,0.25秒睡眠,等等)。这是我视频播放器中的example。
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();
}
}
答案 1 :(得分:5)
您可以通过JMX获取有关CPU和内存使用情况的大量信息,但我认为它不允许任何主动操作。
为了在某种程度上控制CPU使用率,您可以使用Thread.setPriority()。
至于内存,没有每线程内存这样的东西。 Java线程的概念意味着共享内存。控制内存使用的唯一方法是通过-Xmx等命令行选项,但是无法在运行时操作设置。
答案 2 :(得分:5)
照顾Java Forums。基本上计时你的执行,然后等你花太多时间。正如原始线程中所提到的,在一个单独的线程中运行它并中断工作线程将得到更准确的结果,随着时间的推移平均值。
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
}
答案 3 :(得分:1)
您可以为线程分配不同的优先级,以便更频繁地调度最相关的线程。
请看这个answer,看看是否有帮助。
当所有正在运行的线程具有相同的优先级时,它们可以像这样运行:
t1, t2, t3, t1, t2, t3, t1, t2, t3
当您为其中一个分配不同的优先级时,它可能如下所示:
t1, t1, t1, t1, t2, t1, t1, t1 t3.
也就是说,第一个线程“更经常地”运行其余线程。
答案 4 :(得分:1)
如果在单独的进程中运行线程,则可以限制内存使用量并限制CPU数量或更改这些线程的优先级。
但是,您所做的任何事情都可能增加开销和复杂性,而这通常会适得其反。
除非您能解释为什么要这样做(例如,您有一个写得不好的图书馆,您不信任且无法获得支持),我建议您不需要。
不容易限制内存使用的原因是只有一个共享堆。因此,在一个线程中使用的对象可以在另一个线程中使用,而不是分配给一个线程或另一个线程。
限制CPU使用率意味着停止所有线程以便它们不做任何事情,但是更好的方法是确保线程不浪费CPU并且只是活动做需要完成的工作,在这种情况下你我不想阻止他们这样做。
答案 5 :(得分:1)
为什么不进行“线程化”而不进行协作式多任务处理,看看是否可以操作http://www.janino.net/来运行程序一段时间/一组内容,然后停止并运行下一个程序。至少这种方式公平,给每个人同样的时间片......
答案 6 :(得分:0)
Thread.setPriority()可能会有所帮助,但它不允许您限制线程使用的CPU。事实上,我还没有听说过任何Java库这样做。
如果您的线程准备合作,则可以实现此类工具。关键是让线程定期调用自定义调度程序,并使调度程序使用JMX监视线程CPU使用情况。但问题是,如果某些线程没有经常调度调度程序,它可能会超出限制限制。对于一个陷入循环的线程,你无能为力。
实施的另一个理论途径是使用Isolates。不幸的是,您很难找到实现隔离的通用JVM。此外,标准API仅允许您控制隔离,而不是隔离内的线程。
答案 7 :(得分:0)
限制线程CPU使用率的唯一方法是通过资源上的块或经常调用yield()。
这不会将CPU使用率限制在100%以下,但会为其他线程和进程提供更多的时间片。
答案 8 :(得分:-1)
为了减少CPU,你想让你的线程在常见的 if 和 while 循环中休眠。
while(whatever) {
//do something
//Note the capitol 'T' here, this sleeps the current thread.
Thread.sleep(someNumberOfMilliSeconds);
}
睡眠几百毫秒将大大降低CPU使用率,而性能几乎没有明显效果。
至于内存,我会在各个线程上运行一个分析器并进行一些性能调整。如果你限制了线程可用的内存量,我认为可能是内存不足或缺乏线程。我相信JVM可以提供与所需线程一样多的内存,并且通过在任何给定时间只保留范围内的基本对象来减少内存使用量。