Java 为什么以及如何找到JVM堆外内存泄漏发生的位置

Java 为什么以及如何找到JVM堆外内存泄漏发生的位置,java,memory-management,memory-leaks,jvm-crash,off-heap,Java,Memory Management,Memory Leaks,Jvm Crash,Off Heap,我有一个Java项目,它显示了产品环境中的内存泄漏。我们可以看到它的内存使用“top”或“cat/proc/PID/status | grep VmRSS”扩展并越来越大,但它的JVM堆仍处于良好状态,这意味着堆内存由“-Xmx”和其他JVM选项控制。我使用了jmap/dump/jstack和我知道的任何其他命令来分析错误,但都是徒劳的 最后,我们通过逐行测试发现了问题。这是JVM JavaScript引擎中的一个问题。我在这里提取了可以重现问题的代码: package com.unionpay

我有一个Java项目,它显示了产品环境中的内存泄漏。我们可以看到它的内存使用“top”或“cat/proc/PID/status | grep VmRSS”扩展并越来越大,但它的JVM堆仍处于良好状态,这意味着堆内存由“-Xmx”和其他JVM选项控制。我使用了
jmap/dump/jstack
和我知道的任何其他命令来分析错误,但都是徒劳的

最后,我们通过逐行测试发现了问题。这是JVM JavaScript引擎中的一个问题。我在这里提取了可以重现问题的代码:

package com.unionpay.cqp.arch.js;
导入javax.script.*;
导入静态java.lang.Thread.sleep;
公共类JsEngineMain{
私有静态最终ScriptEngine SCRIPT_ENGINE=new ScriptEngineManager().getEngineByName(“JavaScript”);
私有静态最终字符串脚本_FUNC_1=“function max_num(a,b){return(a>b)?a:b;}”;
公共静态void main(字符串[]args){
如果(args==null | | args.length<1){
System.out.println(“错误的参数,使用like:”);
println(“java-jar xxx.jar SLEEP\u TIME\u milies”);
System.out.println(“java-cp xxx.jar com.unionpay.cqp.arch.js.JsEngineMainBak SLEEP_TIME_milies”);
返回;
}
long sleepTime=long.parseLong(args[0]);
试一试{
while(true){
睡眠(睡眠时间);
testLoopFuncString();
}
}捕获(例外e){
e、 printStackTrace();
}
}
私有静态void testLoopFuncString(){
试一试{
可编译的可编译=(可编译的)脚本引擎;
CompiledScript CompiledScript=compileable.compile(脚本函数1);
compiledScript.eval();
Invocable Invocable=(Invocable)脚本引擎;
Object res=invocable.invokeFunction(“max_num”,6,1);
showRes(res);
}捕获(例外e){
e、 printStackTrace();
}
}
私有静态void showRes(对象res){
如果(System.currentTimeMillis()%20000==1){
系统输出打印项次(res);
}
}
}
您可以使用以下命令运行它:

java-Xmx 128M-XX:MaxMetaspaceSize=64M-XX:MaxDirectMemorySize=64M-cp js-engine-test.jar com.unionpay.cqp.arch.js.JsEngineMain 2

并通过以下方式监视内存使用情况:

为true时;执行cat/proc/PID/status | grep VmRSS;睡眠5;完成

然后您将看到内存使用量随着时间的推移而增长

我的问题是,如果我们不知道问题在哪里,如何找到问题?我们如何通过内存分析或命令工具找到错误的代码


我使用
pmap
jcmd
查看内存被占用的位置,我可以从jcmd工具获得一些信息,我知道这是一个堆外问题。但我无法将其与代码连接,因此我无法找到哪行代码不正确。

这听起来像是一个应该分析的问题。尝试获取内存消耗的一般情况,然后尝试分析应用程序的内存,以查看内存的使用位置。当我按照您所述运行命令时,由于行
String cmd=args[1],我得到一个异常(这显然没有任何用途,除了打印一条神秘的消息)。除此之外,您不应该将
System.err
System.out
混合在一条消息中。关于您描述的问题,内存增长并没有错,只要您告诉JVM可以使用该内存,并且没有由于内存不足而导致的实际故障。设置一个紧凑的最大堆内存,并检查它是否在达到该限制时失败,或者只是按照预期执行垃圾收集……很抱歉,由于我的源代码中有一些不相关的代码,因此代码从我的运行代码中简化了。我已经更新了上面的代码。我会像你说的那样做一个测试,但我并不是说jvm有问题,我只是想找出问题出在哪里,或者是哪个地方导致内存越来越大。不管怎么说,这似乎是jvm的问题,可能与Nashorn引擎有关。下面的bug看起来很相似,它是JDK-8081323的副本。这听起来像是一个应该分析的问题。尝试获取内存消耗的一般情况,然后尝试分析应用程序的内存,以查看内存的使用位置。当我按照您所述运行命令时,由于行
String cmd=args[1],我得到一个异常(这显然没有任何用途,除了打印一条神秘的消息)。除此之外,您不应该将
System.err
System.out
混合在一条消息中。关于您描述的问题,内存增长并没有错,只要您告诉JVM可以使用该内存,并且没有由于内存不足而导致的实际故障。设置一个紧凑的最大堆内存,并检查它是否在达到该限制时失败,或者只是按照预期执行垃圾收集……很抱歉,由于我的源代码中有一些不相关的代码,因此代码从我的运行代码中简化了。我已经更新了上面的代码。我会像你说的那样做一个测试,但我并不是说jvm有问题,我只是想找出问题出在哪里,或者是哪个地方导致内存越来越大。不管怎么说,这似乎是jvm的问题,可能与Nashorn引擎有关。下面的错误看起来很相似,它是JDK-8081323的副本