2009-07-29 7 views
37

私は複数のスレッドを実行し、それらのスレッドのCPU /メモリ使用量を抑えたいアプリケーションを作成しています。JavaでのスレッドのCPU /メモリ使用量を調整しますか?

similar question for C++がありますが、可能であればC++とJNIを使​​用しないようにしたいと考えています。私は高級言語を使用してこれが可能ではないかもしれないことに気づいていますが、誰かがアイデアを持っているかどうかは興味があります。

EDIT:賞金が追加されました。私は本当に良い、いくつかのアイデアを考えています。

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

書かれているとおり、実行される各クラスに対して個別のプロセスを作成するのはリファクタリングにとって苦労でしょう。それがVM引数を使ってメモリ使用量を制限する唯一の良い方法であれば、そうすることもできます。しかし、私はスレッドでそれを行う方法があるかどうかを知りたいです。別のプロセスとしても、前にも述べたように、これらのうちのいくつかは一度に実行されるため、何とかCPU使用量を制限できるようにしたいと考えています。私は、無限ループがすべてのリソースを掘り起こすことを望んでいません。

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

+0

スレッディングモデルには何を使用していますか? Javaのタスク実行プログラムですか? –

+0

また、このアプリケーションのボトルネックはどこですか?データベース? IO? –

+0

バッテリの寿命が問題であった場合、CPUの上限を設定したいと思う唯一の時間がありました(そして、あなたの質問は、バッテリー制限のあるデバイスで計算上高価なものをやっているときにどうすればわかりますか? 。それ以外の場合は、ユーザーが必要以上に長く待つのはなぜですか?システムの応答性を維持したい場合は、CPU使用量を制限するのではなく、スレッドの優先度を低くしてください。 –

答えて

30

私があなたの問題を理解している場合、ビデオ再生がJavaで行われるのと同様に、スレッドを適応的にスリープさせることが1つの方法です。コア使用率を50%にすることがわかっている場合、アルゴリズムは約0.5秒スリープ状態になります.1秒以内に潜在的に分散されます(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(); 
    } 
} 
+0

私はこれを人々がすばやく見ることをより容易にしたいと思います。あなたがやっていることについてもう少し詳細を追加してコードをコメントしてもよろしいですか? –

+0

サンプルコードがコメントなしですでに長すぎるため、あまり役に立ちません。しかし、私はそれをやります。 – akarnokd

+0

これはメモリ管理を扱う本当に巧妙な方法であり、これまで誰にも与えられた唯一の良い方向です。私のOPの編集2のために私にとってはうまくいきませんが、私はおそらくAspectsを使って、そのスレッド用に作成された新しいオブジェクトを傍受し、各スレッドのセマフォで追跡します。 –

5

JMXでCPUとメモリの使用について多くの情報を得ることができますが、アクティブな操作は許可されていないと思います。

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

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

1

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

このanswerをご覧ください。すべての実行中のスレッドの優先度が同じ場合は

彼らは次のように実行することがあります。です

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

:あなたがそれらのいずれかに異なる優先順位を割り当てると

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

それはのように見えるかもしれ最初のスレッドは残りのスレッドよりも「頻繁に」実行されます。

+0

実際には、Javaでは通常、すべてのスレッドが同じ優先順位で実行されるため、これは役に立ちません。したがって、優先順位は通常無視されます。 –

0

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

スレッドが協調する準備ができていれば、このような機能を実装することは可能かもしれません。重要なのは、スレッドが定期的にカスタムスケジューラを呼び出し、JMXを使用してスケジューラがスレッドCPU使用率を監視するようにすることです。しかし、問題は、あるスレッドがスケジューラー呼び出しを十分に頻繁に行わない場合、スレッド制限を十分に上回る可能性があるということです。そして、ループに詰まったスレッドについては何もできません。

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

0

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

これは、CPU使用率を100%未満に制限するものではなく、他のスレッドとプロセスでより多くのタイムスライスを提供します。

-1

CPUを減らすために、あなたは whileループと場合共通の内側にあなたのスレッドをスリープ状態にしたいです。

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

数百ミリ秒間スリープすると、CPU使用率が大幅に低下し、パフォーマンスにほとんど影響はありません。

メモリに関しては、私は個々のスレッドでプロファイラを実行し、いくつかのパフォーマンスチューニングを行います。あなたがスレッドのメモリ使用量を抑えてしまった場合、メモリ不足またはスレッド不足が考えられます。私はJVMが必要とするスレッドと同じくらい多くのメモリを提供し、いつでも重要なオブジェクトのみをスコープに保持することによってメモリ使用量を減らすことに努めています。

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 
} 
1

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

しかし、あなたが行うことは、オーバーヘッドや複雑さを増やす可能性があります。これはしばしば逆効果です。

あなたがこれをやりたい理由を説明できない場合(たとえば、信頼できないし、サポートを受けることができない、ひどく書かれた図書館がある場合など)、私はあなたにする必要はないとお勧めします。

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

CPUの使用を制限するということは、すべてのスレッドを停止することで、何もしないようにすることですが、より良い方法は、スレッドがCPUを無駄にしないようにすることです。あなたはそれをやめさせたくないでしょう。

+1

これにOPの推論を追加しました。 –

+0

無限ループは1つのコアのみを消費します。多くの新しいサーバーには4〜16個のコアがあるため、これまでのような問題はないかもしれません。注:何らかの理由で独立したプロセスを安全に終了させることができます。 –

1

「スレッド化」を行う代わりに、協力マルチタスクを行うのは、特定の時間/命令のセットを実行するためにhttp://www.janino.net/を操作してから、次のプログラムを停止して実行するのが面白いでしょう。少なくとも公正な方法で、皆に同じタイムスライスを与えてください。

関連する問題