実行時にJVMが使用可能な残りのメモリを取得する良い方法はありますか?この使用例は、OutOfMemoryエラーで突然死ぬのではなく、「これを使用している人が多すぎます。後で再試行してください」というニースエラーメッセージで新しい接続を拒否することにより、メモリ制限に近づいたときに正常に失敗するWebサービスを使用することです。
これは、各オブジェクトのコストを事前に計算/推定することとは関係ありません。原則として、その推定に基づいて、オブジェクトがどのくらいのメモリを使用し、新しい接続を拒否するかを推定できましたが、それは一種のハッキング/脆弱なようです。
William Brendelによるこのサンプルは、何か役に立つかもしれません。
編集:私はもともとこのサンプルを提供しました(別のトピックに関するウィリアム・ブレンデルの回答へのリンク)。そのトピックの作成者(Steve M)は、マルチプラットフォームJavaアプリケーションの作成を望んでいました。具体的には、ユーザーは実行中のマシンのリソース(ディスク容量、CPUおよびメモリ使用量)を評価する手段を見つけようとしました。
これは、そのトピックで与えられた回答のインライン転写です。ただし、このトピックでは、私の回答が承認済みとマークされているにもかかわらず、理想的なソリューションではないことが指摘されています。
_public class Main {
public static void main(String[] args) {
/* Total number of processors or cores available to the JVM */
System.out.println("Available processors (cores): " +
Runtime.getRuntime().availableProcessors());
/* Total amount of free memory available to the JVM */
System.out.println("Free memory (bytes): " +
Runtime.getRuntime().freeMemory());
/* This will return Long.MAX_VALUE if there is no preset limit */
long maxMemory = Runtime.getRuntime().maxMemory();
/* Maximum amount of memory the JVM will attempt to use */
System.out.println("Maximum memory (bytes): " +
(maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
/* Total memory currently in use by the JVM */
System.out.println("Total memory (bytes): " +
Runtime.getRuntime().totalMemory());
/* Get a list of all filesystem roots on this system */
File[] roots = File.listRoots();
/* For each filesystem root, print some info */
for (File root : roots) {
System.out.println("File system root: " + root.getAbsolutePath());
System.out.println("Total space (bytes): " + root.getTotalSpace());
System.out.println("Free space (bytes): " + root.getFreeSpace());
System.out.println("Usable space (bytes): " + root.getUsableSpace());
}
}
}
_
ユーザーChristian Friesは、Runtime.getRuntime().freeMemory()
がメモリ不足エラーが発生するまで割り当てられるメモリ量を提供すると仮定するのは間違っていると指摘しています。
documentation から、Runtime.getRuntime().freeMemory()
の署名の戻り値は次のとおりです。
Returns:バイト単位で測定される、将来割り当てられるオブジェクトに現在利用可能なメモリの総量の概算。
ただし、ユーザーChristian Friesは、この機能が誤って解釈されている可能性があると主張しています。彼は、メモリ不足エラーが発生するまで割り当てられるメモリのおおよその量(空きメモリ)は、次のように与えられる可能性が高いと主張しています。
_long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
_
allocatedMemory
は以下によって与えられます:
_long allocatedMemory =
(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
_
ここで重要なのは、空きメモリの概念間の矛盾です。 1つのことは、オペレーティングシステムがJava仮想マシンに提供するメモリです。もう1つは、Java仮想マシン自体が実際に使用しているメモリブロックのチャンクを含む合計バイト数です。
Javaアプリケーションに与えられたメモリはJava仮想マシンによってブロック単位で管理されることを考慮すると、Java仮想マシンが利用できる空きメモリの量は正確ではない可能性がありますJavaアプリケーションで使用可能なメモリと一致します。
具体的には、Christian Friesは_-mx
_または_-Xmx
_フラグの使用を示して、Java仮想マシンで使用可能な最大メモリ量を設定します。彼は、次の機能の違いに注目しています。
_/* Returns the maximum amount of memory available to
the Java Virtual Machine set by the '-mx' or '-Xmx' flags. */
Runtime.getRuntime().maxMemory();
/* Returns the total memory allocated from the system
(which can at most reach the maximum memory value
returned by the previous function). */
Runtime.getRuntime().totalMemory();
/* Returns the free memory *within* the total memory
returned by the previous function. */
Runtime.getRuntime().freeMemory();
_
クリスチャンは、Runtime.getRuntime().freeMemory()
が実際に推定可能な空きメモリと呼ばれるものを返すと述べて、彼の答えを締めくくりました。将来のメモリ割り当てがその関数によって返される値を超えない場合でも、Java仮想マシンがホストシステムによって割り当てられた実際のメモリチャンクをまだ受信していない場合、 _Java.lang.OutOfMemoryError
_ は引き続き生成されます。
最終的に、使用する適切な方法は、アプリケーションの詳細に依存する度合いが異なります。
役に立つかもしれない別のリンクを提供します。これはユーザーRichard Dormandが作成した質問で、stones333が回答しました 使用されているデフォルトのJavaヒープサイズの決定について
注:これまでのすべての回答は、受け入れられたものも含めて、Runtime.getRuntime().freeMemory()
がメモリ不足エラーが発生するまで割り当てられるメモリ量を与えると言って質問に答えているようです。ただし、これは間違っています。
approximateメモリ不足エラーが発生するまで割り当てられる可能性のあるメモリ量、つまり空きメモリが
_long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;
_
どこ
_long allocatedMemory = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());
_
説明: -mxパラメーター(または-Xmx)を介してJVMを起動する場合、JVMで利用可能な最大量を指定します。 Runtime.getRuntime().maxMemory()
はこの量を提供します。このシステムメモリ量から、JVMはメモリをチャンク、たとえば64 mbのブロックに割り当てます。開始時に、JVMはシステムからそのようなチャンクのみを割り当て、全量は割り当てません。 Runtime.getRuntime().totalMemory()
はシステムから割り当てられた合計メモリを提供し、Runtime.getRuntime().freeMemory()
は空きメモリを提供しますwithin割り当てられた合計メモリ。
したがって:
_long definitelyFreeMemory = Runtime.getRuntime().freeMemory();
_
は、JVMによってすでに予約されている空きメモリですが、おそらくわずかな量です。そして、おそらくpresumableFreeMemory
を取得します。もちろん、presumableFreeMemory
よりも少ない量を割り当てようとしても、メモリ不足の例外が発生する場合があります。これは、JVMがシステムから次のメモリチャンクを取得しない場合に発生する可能性があります。ただし、ほとんどのシステムではこれは決して起こらず、システムはむしろスワップを開始します-避けたい状況です。 W.r.t.元の質問:-mxが適切な値に設定されている場合、presumableFreeMemory
は空きメモリの適切なインジケータです。
ランタイムメソッドの使用に加えて、次を使用して追加のメモリ情報を取得できます。
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memBean.getHeapMemoryUsage();
MemoryUsage nonheap = memBean.getNonHeapMemoryUsage();
各 MemoryUsage は、init、used、committed、およびmaxの値を提供します。これは、メモリをポーリングして記録するメモリモニタスレッドを作成し、メモリ使用量の履歴を長期にわたって提供する場合に役立ちます。エラーに至るまでのメモリ使用量を経時的に確認すると役立つ場合があります。
これを極端にしたい場合は、ヒープダンプスレッドを作成します。時間の経過とともにメモリ使用量を監視し、特定のしきい値を超えた場合、以下を実行します(これはJBoss 5.0で動作します-走行距離は異なる場合があります)。
// init code
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean diagBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.Sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class);
// loop code
// add some code to figure if we have passed some threshold, then
File heapFile = new File(outputDir, "heap-" + curThreshold + ".hprof");
log.info("Dumping heap file " + heapFile.getAbsolutePath());
diagBean.dumpHeap(heapFile.getAbsolutePath(), true);
後で、Eclipse memory analyser または同様のツールでメモリリークなどをチェックして、これらのヒープダンプファイルを確認できます。
他の答えに加えて、アプリに SoftReferences を使用するキャッシュがある可能性があるため、これを行うことは必ずしも良いアイデアではないことに注意してください。
このようなキャッシュは、JVMがメモリ制限に達するとすぐにメモリを解放します。十分な空きメモリがなくても、メモリを割り当てると、最初にソフト参照によってメモリが解放され、割り当てに使用できるようになります。
OS全体で利用可能なメモリを取得するには、次のmaven依存関係を使用して OSHI
を追加します。
<dependency>
<groupId>com.github.oshi</groupId>
<artifactId>oshi-core</artifactId>
<version>LATEST</version>
</dependency>
次に、Javaで次のコードを使用します。
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
long availableMemory = hal.getMemory().getAvailable();
いつでもRuntime.getRuntime().freeMemory()
を呼び出すことができます。
問題の残りの半分であるオブジェクトのコストを取得することは、私にとってより問題が多いようです。
より良い解決策は、新しい接続を拒否することなく定格負荷の150%を適切に受け入れることができるように、Webサービスをクラスター化およびスケーリングする方法を見つけることだと思います。サイジングの練習は、コードハックよりも優れたソリューションを得るように思えます。
Runtime.getRuntime().freeMemory()
は、実行中のその時点で JVMの空きメモリ を取得する方法です。それは良い方法ですか(または)アプリケーションに完全に依存していないか。
long freeMemory = Runtime.getRuntime().freeMemory();