Java実行Runtime.exec(shell)報Cannot allocate memory

2861 ワード

Linuxの下でjavaのRuntime.getRuntime().exec(cmd)方式でshellスクリプトを実行すると、「Cannot allocate memory」というエラーが発生します.
ネット上の検索資料は以下のように整理されている.
Cannot allocate memory
LinuxでJavaと呼ばれる複雑なJavaプログラムをデバッグしましょう.JavaAはProcess proc=Runtime.getRuntime().exec(cmd)を頻繁に通過します.外部プログラムを呼び出します.システムの負荷とプログラムのメモリ消費量が比較的大きい場合、呼び出しに失敗する場合があります.エラーメッセージは「Cannot allocate memory」です.
overcommit_memory
topにより,Java Aのほとんどの時間が消費するメモリは実際には多くないが,消費する仮想メモリは大きいことが分かった.このプログラムの起動時のJVMパラメータをすぐに修正し、最大メモリを小さくすると、やはりエラーは発生しません.JavaAはメモリに大量のデータを処理しなければならないため、メモリが小さすぎると処理できない可能性があるため、このように変更することはできません.
インターネットで調べているうちに、面白いものが見つかりました.Linuxカーネルでメモリを設定できるovercommit_memoryプロパティ(設定方法:echo 1>/proc/sys/vm/overcommit_memory)は、Linuxカーネルが保守的で、常に多くのメモリを申請しているが、実際には使用されていないことを意味するため、overcommitを設定すると、カーネルは残りのメモリが十分かどうかをチェックせず、すべてのメモリ割り当てを直接許可する.ほとんどの場合問題ないかもしれませんが、よく考えてみると、mallocの意味が変更され、呼び出し元は値を返すことでメモリの割り当てに成功したかどうかを判断できません.もう一つの問題は、もしメモリが本当に足りなかったらどうしますか?Linuxには特殊なプロセスがあり、OOM(out-of-memory)プロセスが終了した場合、メモリが本当に足りない場合、ランダムに、またはいくつかの原則に基づいてプロセスを殺す機能があります.選択プロセスの原則が正確に制御できないようで、それは恐ろしいことです.の
Runtime.getRuntime().exec(cmd)の実行フロー分析
Javaプログラムが外部プログラムを呼び出すときに、親プロセスと同じサイズのメモリを割り当てる必要がある可能性があることを意味します.これはおかしいですね.例えば、lsコマンドを勝手に呼び出しても、メモリがたくさん必要ですか.Java呼び出し外部プログラムのインタフェースで処理するのは特殊に違いない.ええ、ちょうどJDKもオープンして、ソースコードを見て行きます.
SUN JDK 1.5 SRCを分析し、Runtime.getRuntime().exec(cmd)の実行フローを見つけます.
java.lang.Runtime.exec(cmd);
--java.lang.ProcessBuilder.start();
----java.lang.ProcessImpl.start();
------Java_java_lang_UNIXProcess_forkAndExec() in j2se/src/solaris/native/java/lang/UNIXProcess_md.c
--------1). fork(); 2). execvp();
man forkは、forkが生成したサブプロセスが親プロセスのメモリ内のすべてのデータ内容(コードセグメント、データセグメント、スタックセグメント)をコピーする必要があることを知っています.すべてのコピーのオーバーヘッドが大きいため、Linuxはすでにcopy-on-writeメカニズムを採用しています.つまり、ページ表をコピーし、内容を共有し、変更があったときにメモリを申請し、データをコピーします.
そこで私は、問題の原因は、Linuxがfork()でcopy-on-writeメカニズムを採用しているにもかかわらず、JVMがfork()を呼び出すと、Javaプロセスの他のスレッドがスケジューリングされて実行を続け、自分のメモリを修正していることが多いが、このときexecvp()はまだ実行されていないため、悲劇が発生し、メモリが再コピーされる可能性があると分析した.
 
解決策
方法一(推奨しない):overcommitを変更するmemory値:echo 1>/proc/sys/vm/overcommit_memory;
方法2: 問題が発生した場合、親プロセスと同じサイズのメモリの割り当てを申請する可能性があるため、親プロセスが使用するメモリを制限すればいいです.開発中のJavaAは比較的大きなメモリを使用する必要がありますが、JavaAは必ずしも親プロセスではありません.JavaBと呼ばれるJavaプログラムを単独で実行することができます.外部プログラムの呼び出しを担当します.JavaAはパッケージされたインタフェースを呼び出して通信し、外部プログラムが終了するのを待って、Runtime.getRuntime().exec(cmd)の意味と一致します.この単独で実行するJavaBは小さなメモリしか必要としないため、メモリの割り当てができず、外部プログラムが実行できないという問題が発生する可能性は低い.
方法3:TomcatのWebアプリケーションに対して、catalina.shでJVMのメモリ最適化構成を行うことに慣れています.
JAVA_OPTS="$JAVA_OPTS -Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms256m -Xmx1024m -XX:NewSize=64m -XX:MaxNewSize=512m -XX:PermSize=64m -XX:MaxPermSize=512m -XX:+DisableExplicitGC"

-Xms、-XX:NewSize、-XX:PermSizeのメモリサイズを小さく設定すると、表示されないことが保証されます. Cannot allocate memory異常.