JavaでスレッドのCPU /メモリ使用量を調整しますか?
-
05-07-2019 - |
質問
複数のスレッドを実行するアプリケーションを作成しており、それらのスレッドのCPU /メモリ使用量を調整したい。
C ++に関する同様の質問がありますが、C ++の使用を避けたい可能であればJNI。高級言語ではこれは不可能かもしれませんが、アイデアがあるかどうか知りたいです。
編集:報奨金を追加しました。これについては、本当によく考え抜かれたアイデアが欲しいです。
編集2:これが必要な状況は、サーバーで他の人のコードを実行することです。基本的にそれは完全に任意のコードであり、唯一の保証はクラスファイルにメインメソッドがあることです。現在、実行時にロードされる複数の完全に異なるクラスは、個別のスレッドとして同時に実行されています。
それが書かれている方法では、実行されるクラスごとに個別のプロセスを作成するためにリファクタリングするのは苦痛です。それがVM引数を介してメモリ使用量を制限する唯一の良い方法であるなら、そうである。しかし、スレッドでそれを行う方法があるかどうかを知りたいです。別個のプロセスとしても、CPU使用率を何らかの方法で制限できるようにしたいと思います。前述したように、これらのいくつかは一度に実行されるからです。無限ループがすべてのリソースを独占することは望ましくありません。
編集3:オブジェクトのサイズを概算する簡単な方法は、javaの Instrumentation クラス。具体的には、getObjectSizeメソッド。このツールを使用するには、特別な設定が必要なことに注意してください。
解決
あなたの問題を理解している場合、ビデオの再生がJavaで行われるのと同様に、1つの方法はスレッドを適応的にスリープ状態にすることです。 50%のコア使用率が必要であることがわかっている場合、アルゴリズムは約0.5秒スリープする必要があります-1秒以内に分散される可能性があります(例:0.25秒の計算、0.25秒のスリープ、e.t.c。)。 動画プレーヤーの例。
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();
}
}
他のヒント
JMX ですが、積極的に操作できるとは思いません。
CPU使用率をある程度制御するには、 Thread.setPriority()。
メモリに関しては、スレッドごとのメモリのようなものはありません。 Javaスレッドの概念そのものが共有メモリを意味します。メモリ使用量を制御する唯一の方法は-Xmxなどのコマンドラインオプションを使用することですが、実行時に設定を操作する方法はありません。
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
}
スレッドに異なる優先順位を割り当てると、最も関連性の高いスレッドがより頻繁にスケジュールされるようになります。
こちらをご覧ください回答で解決するかどうかを確認します。
実行中のすべてのスレッドの優先度が同じ場合、次のように実行できます。
t1, t2, t3, t1, t2, t3, t1, t2, t3
それらのいずれかに異なる優先度を割り当てると、次のようになります:
t1, t1, t1, t1, t2, t1, t1, t1 t3.
つまり、最初のスレッドが「より頻繁に」実行されます。その残り。
別のプロセスでスレッドを実行する場合、メモリ使用量を制限してCPUの数を制限するか、これらのスレッドの優先順位を変更できます。
ただし、あなたがすることはオーバーヘッドと複雑さを追加する可能性が高く、しばしば逆効果になります。
これを行う理由を説明できない限り(たとえば、信頼できず、サポートを受けられないひどく書かれたライブラリがある場合)、不要にすることをお勧めします。
メモリ使用量を制限するのが簡単ではない理由は、共有されるヒープが1つしかないことです。したがって、あるスレッドで使用されるオブジェクトは別のスレッドで使用可能であり、あるスレッドや別のスレッドには割り当てられません。
CPU使用量の制限とは、すべてのスレッドを停止して何も行わないことを意味しますが、スレッドがCPUを無駄にせず、実行する必要がある作業のみをアクティブにすることをお勧めします。彼らがやるのを止めたくないでしょう。
「スレッド」を行う代わりに協調的なマルチタスクを行うには、 http://www.janino.net/ を操作できるかどうかを確認してください。特定の時間/命令セットでプログラムを実行し、次のプログラムを停止して実行します。少なくともそのように公平な、皆に同じタイムスライスを与える...
Thread.setPriority()が役立つ場合がありますが、スレッドが使用するCPUを制限することはできません。実際、これを行うJavaライブラリは聞いたことがありません。
スレッドが協力する準備ができていれば、このような機能を実装することが可能かもしれません。重要なのは、スレッドが定期的にカスタムスケジューラーを呼び出し、スケジューラーがJMXを使用してスレッドのCPU使用率を監視することです。しかし、問題は、あるスレッドがスケジューラー呼び出しを頻繁に行わない場合、スロットル制限を十分に超える可能性があることです。そして、ループに陥るスレッドについてできることは何もありません。
実装する別の理論的なルートは、分離株を使用することです。残念ながら、分離株を実装する汎用JVMを見つけるのは難しいでしょう。また、標準APIでは、分離内のスレッドではなく分離のみを制御できます。
スレッドのCPU使用率を制限できる唯一の方法は、リソースをブロックするか、またはyield()を頻繁に呼び出すことです。
これにより、CPU使用率が100%未満に制限されることはありませんが、他のスレッドとプロセスにより多くのタイムスライスが与えられます。
CPUを削減するには、一般的な if および while ループ内でスレッドをスリープさせます。
while(whatever) {
//do something
//Note the capitol 'T' here, this sleeps the current thread.
Thread.sleep(someNumberOfMilliSeconds);
}
数百ミリ秒スリープすると、CPU使用量が大幅に削減されますが、パフォーマンスに目立った結果はほとんどありません。
メモリについては、個々のスレッドでプロファイラーを実行し、パフォーマンスチューニングを行います。スレッドで使用可能なメモリの量を調整した場合、メモリ不足の例外またはスレッドが不足している可能性が高いと思います。 JVMが必要とするスレッドと同じ量のメモリを提供することを信頼し、必要なオブジェクトのみを常にスコープ内に保持することでメモリ使用量の削減に取り組みます。