41

いくつかのコレクションで動作する、重い処理を行うクラスがあると仮定します。私がやりたいことは、そのような操作がメモリ不足につながることがないようにすることです。さらに良いことに、使用できるメモリ量のしきい値を設定したいと考えています。

class MyClass()
{
   public void myMethod()
   {
      for(int i=0; i<10000000; i++)
      {
         // Allocate some memory, may be several collections
      }
   }
}

class MyClassTest
{
   @Test
   public void myMethod_makeSureMemoryFootprintIsNotBiggerThanMax()
   {
      new MyClass().myMethod(); 
      // How do I measure amount of memory it may try to allocate?
   }
}

これを行うための正しいアプローチは何ですか? それとも、これは不可能/実現不可能ですか?

4

7 に答える 7

23

いくつかのオプションを考えることができます:

  • マイクロベンチマーク(つまりjmh ) を使用して、メソッドが必要とするメモリ量を確認します。
  • ヒューリスティックな推定に基づく割り当て戦略の構築。クラス サイズの推定、つまりClassSizeを実装するオープン ソース ソリューションがいくつかあります。はるかに簡単な方法は、めったに使用されないオブジェクトを解放するキャッシュ (つまり、Guava のキャッシュ) を利用することです。@EnnoShioji が述べたように、Guava のキャッシュにはメモリベースのエビクション ポリシーがあります。

メモリをカウントする独自のベンチマーク テストを作成することもできます。アイデアは

  1. シングルスレッドを実行します。
  2. 割り当てるオブジェクトを格納する新しい配列を作成します。したがって、これらのオブジェクトは GC の実行中に収集されません。
  3. System.gc()memoryBefore = runtime.totalMemory() - runtime.freeMemory()
  4. オブジェクトを割り当てます。それらを配列に入れます。
  5. System.gc()memoryAfter = runtime.totalMemory() - runtime.freeMemory()

これは、メモリ割り当てをバイト精度で測定できる軽量のマイクロ ベンチマーク ツールで使用した手法です。

于 2013-11-05T09:41:47.427 に答える
5

現在のメモリ使用量を測定するには、次を使用します。

Runtime.getRuntime().freeMemory()Runtime.getRuntime().totalMemory()

良い例を次に示し ます。OS レベルのシステム情報を取得します。

ただし、この測定は正確ではありませんが、多くの情報を提供できます。もう 1 つの問題は、GCどちらが予測できないかということです。

于 2013-11-05T09:20:40.287 に答える
2

同様のことを行う Netty の例を次に示します: MemoryAwareThreadPoolExecutor。Guava のキャッシュ クラスには、サイズ ベースのエビクションもあります。これらのソースを見て、彼らがしていることをコピーできます。特に、 Netty がオブジェクトのサイズを推定する方法は次のとおりです。本質的には、メソッドで生成するオブジェクトのサイズを見積もり、カウントを維持します。

全体的なメモリ情報 (利用可能な/使用されているヒープの量など) を取得すると、メソッドに割り当てるメモリ使用量を決定するのに役立ちますが、個々のメソッド呼び出しで使用されたメモリ量を追跡することはできません。

そうは言っても、これが合法的に必要になることは非常にまれです。ほとんどの場合、特定の時点で存在できるオブジェクトの数を制限することによってメモリ使用量を制限する (たとえば、制限付きキューを使用する) ことで十分であり、実装がはるかに簡単です。

于 2013-11-05T09:20:46.850 に答える
0

メモリ使用量を見積もる最も簡単な方法は、Runtimeクラスのメソッドを使用することです。

これに頼らないことをお勧めしますが、おおよその見積もりにのみ使用してください。理想的には、この情報をログに記録して独自に分析し、テストやコードの自動化には使用しないでください。

あまり信頼できないかもしれませんが、単体テストのような閉じた環境では、現実に近い見積もりが得られるかもしれません。
特に、System.gc()ガベージ コレクターを呼び出した後、期待どおりに実行されるという保証はありません (これは GC の提案にすぎません)。そこにfreeMemory記載されているメソッドには精度の制限があります。さらに注意事項があるかもしれません。

解決:

private static final long BYTE_TO_MB_CONVERSION_VALUE = 1024 * 1024;

@Test
public void memoryUsageTest() {
  long memoryUsageBeforeLoadingData = getCurrentlyUsedMemory();
  log.debug("Used memory before loading some data: " + memoryUsageBeforeLoadingData + " MB");
  List<SomeObject> somethingBigLoadedFromDatabase = loadSomethingBigFromDatabase();
  long memoryUsageAfterLoadingData = getCurrentlyUsedMemory();
  log.debug("Used memory after loading some data: " + memoryUsageAfterLoadingData + " MB");
  log.debug("Difference: " + (memoryUsageAfterLoadingData - memoryUsageBeforeLoadingData) + " MB");
  someOperations(somethingBigLoadedFromDatabase);
}

private long getCurrentlyUsedMemory() {
  System.gc();
  return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / BYTE_TO_MB_CONVERSION_VALUE;
}
于 2021-01-06T20:37:45.763 に答える