web-dev-qa-db-ja.com

JavaでスレッドのCPU /メモリ使用量を抑制しますか?

複数のスレッドを実行するアプリケーションを作成していて、それらのスレッドのCPU /メモリ使用量を抑制したいと考えています。

C++の同様の質問 がありますが、可能であればC++とJNIの使用を避けたいと思います。高級言語ではこれは不可能かもしれませんが、誰かが何かアイデアを持っているかどうか知りたいです。

EDIT:バウンティを追加しました。私はこれについてよく考えられ、よく考えられたアイデアをいくつかお願いします。

EDIT 2:これが必要な状況は、私のサーバーで他の人のコードを実行していることです。基本的にそれは完全に任意のコードであり、唯一の保証はクラスファイルにmainメソッドがあることです。現在、実行時に読み込まれる複数の完全に異なるクラスは、別々のスレッドとして同時に実行されています。

その記述方法では、実行されるクラスごとに個別のプロセスを作成することをリファクタリングするのは面倒です。 VM引数を介してメモリ使用量を制限する唯一の良い方法である場合は、それもそうです。しかし、スレッドでそれを行う方法があるかどうか知りたいのです。前述のように、これらのいくつかは一度に実行されるので、CPU使用率を何らかの形で制限できるようにしたいのですが、無限ループがすべてのリソースを占有するのは望ましくありません。

EDIT 3:オブジェクトのサイズを概算する簡単な方法は、Javaの Instrumentation クラスを使用することです。具体的には、getObjectSizeメソッド。このツールを使用するには、特別な設定が必要です。

43
Alex Beardsley

私があなたの問題を理解しているなら、Javaでビデオ再生が行われるのと同じように、スレッドを適応的にスリープさせる方法が1つあるでしょう。 50%のコア使用率が必要であることがわかっている場合、アルゴリズムは約0.5秒スリープする必要があります-1秒以内に分散される可能性があります(たとえば、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();
    }
}
32
akarnokd

世話 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
}
5
Andrew

[〜#〜] jmx [〜#〜] を使用して、CPUとメモリの使用状況に関する多くの情報を取得できますが、アクティブな操作が可能だとは思いません。

CPU使用率をある程度制御するには、 Thread.setPriority() を使用できます。

メモリに関しては、スレッドごとのメモリなどはありません。 Javaスレッドの概念そのものが共有メモリを意味します。メモリの使用を制御する唯一の方法は、-Xmxなどのコマンドラインオプションを使用することですが、実行時に設定を操作する方法はありません。

5

スレッドにさまざまな優先順位を割り当てて、最も関連性の高いスレッドがより頻繁にスケジュールされるようにすることができます。

answer を見て、それが役立つかどうかを確認してください。

実行中のすべてのスレッドが同じ優先度を持っている場合、これらは次のように実行されます。

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

それらの1つに異なる優先度を割り当てると、次のようになります。

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

つまり、最初のスレッドは残りのスレッドよりも「より頻繁に」実行されます。

1
OscarRyz

別のプロセスでスレッドを実行する場合は、メモリ使用量を制限してCPUの数を制限するか、これらのスレッドの優先度を変更できます。

ただし、何かを行うとオーバーヘッドや複雑さが増し、逆効果になることがよくあります。

なぜこれを実行するのかを説明できない限り(たとえば、信頼できないライブラリが適切に作成されておらず、サポートを受けられない場合)、必要はないことをお勧めします。

メモリ使用量を制限するのが容易ではない理由は、共有されるヒープが1つしかないためです。したがって、あるスレッドで使用されているオブジェクトは別のスレッドで使用でき、あるスレッドまたは別のスレッドに割り当てられていません。

CPU使用率を制限することは、すべてのスレッドを停止して何もしないようにすることを意味しますが、スレッドがCPUを浪費せず、実行する必要がある作業のみをアクティブにすることをお勧めします。彼らがそれをやめたいと思っています。

1
Peter Lawrey

「スレッディング」を行う代わりに協調マルチタスクを行うのではなく、 http://www.janino.net/ を操作して、一定時間プログラムを実行できるかどうかを確認すると興味深いでしょう命令の次に、停止して次のプログラムを実行します。少なくともそのように公平に、全員に同じタイムスライスを与えてください...

1
JH.

Thread.setPriority()は役立つかもしれませんが、スレッドが使用するCPUを制限することはできません。実際、これを行うJavaライブラリは聞いたことがありません。

スレッドが連携する準備ができていれば、このような機能を実装することが可能かもしれません。重要なのは、スレッドが定期的にカスタムスケジューラーを呼び出し、スケジューラーがJMXを使用してスレッドのCPU使用率を監視するようにすることです。しかし、問題は、一部のスレッドがスケジューラを頻繁に呼び出さない場合、スロットル制限を十分に超える可能性があることです。そして、ループに陥ったスレッドに対してできることは何もありません。

実装への別の理論的なルートは、分離を使用することです。残念ながら、分離株を実装する汎用のJVMを見つけるのは難しいでしょう。また、標準APIでは、分離領域内のスレッドではなく、分離領域のみを制御できます。

0
Stephen C

スレッドCPUの使用を制限できる唯一の方法は、リソースをブロックするか、yield()を頻繁に呼び出すことです。

これにより、CPU使用率が100%未満に制限されることはありませんが、他のスレッドと処理により多くのタイムスライスが与えられます。