製品環境でメモリ リークを示す Java プロジェクトがあります。'top' または 'cat /proc/PID/status|grep VmRSS' を使用してメモリが拡大し、どんどん大きくなるのを確認できますが、JVM ヒープは良好な状態のままです。つまり、ヒープ メモリは '- Xmx' およびその他の JVM オプション。jmap/dump/jstack
何が悪いのかを分析するために、私が知っている他のコマンドを使用しましたが、無駄でした。
最後に、行ごとにテストして問題を発見しました。これは JVM JavaScript エンジンの問題です。問題を再現できるコードをここから抽出しました。
package com.unionpay.cqp.arch.js;
import javax.script.*;
import static java.lang.Thread.sleep;
public class JsEngineMain {
private static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("JavaScript");
private static final String SCRIPT_FUNC_1 = "function max_num(a, b){return (a>b)?a:b;} ";
public static void main(String[] args) {
if (args == null || args.length < 1) {
System.out.println("wrong args, use like:");
System.out.println("java -jar xxx.jar SLEEP_TIME_MILLIES");
System.out.println("java -cp xxx.jar com.unionpay.cqp.arch.js.JsEngineMainBak SLEEP_TIME_MILLIES");
return;
}
long sleepTime = Long.parseLong(args[0]);
try {
while (true) {
sleep(sleepTime);
testLoopFuncString();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static void testLoopFuncString() {
try {
Compilable compilable = (Compilable) SCRIPT_ENGINE;
CompiledScript compiledScript = compilable.compile(SCRIPT_FUNC_1);
compiledScript.eval();
Invocable invocable = (Invocable) SCRIPT_ENGINE;
Object res = invocable.invokeFunction("max_num", 6, 1);
showRes(res);
} catch (Exception e) {
e.printStackTrace();
}
}
private static void showRes(Object res) {
if (System.currentTimeMillis() % 20000 == 1) {
System.out.println(res);
}
}
}
次のコマンドで実行できます。
java -Xmx 128M -XX:MaxMetaspaceSize=64M -XX:MaxDirectMemorySize=64M -cp js-engine-test.jar com.unionpay.cqp.arch.js.JsEngineMain 2
次の方法でメモリ使用量を監視します。
while true; do cat /proc/PID/status|grep VmRSS;sleep 5;done
その後、メモリ使用量が時間の経過とともに増加することがわかります。
さて、私の質問は、問題がどこにあるのかわからない場合、どうすれば問題を見つけることができるでしょうか? また、メモリ分析やコマンド ツールを使用して、正しくないコードを見つけるにはどうすればよいでしょうか。
pmap
およびを使用しjcmd
て、メモリが占有されている場所を確認します。jcmd ツールから情報を取得でき、オフヒープの問題であることがわかります。しかし、コードで接続できないため、コードのどの行が間違っているかを見つけることができません。