本机 C 和 Fortran 代码的 Java 内存泄漏
Posted
技术标签:
【中文标题】本机 C 和 Fortran 代码的 Java 内存泄漏【英文标题】:Java memory leak with native C and Fortran code 【发布时间】:2014-04-14 12:10:18 【问题描述】:我正在开发一个旧的 java 程序,其中包含一个带有 Fortran 调用的本机库。
所以,我有 Java 通过 JNI 调用 C,然后调用 Fortran。
在生产中,我们有一个内存不足错误,例如:
Native memory allocation (malloc) failed to allocate 120000 bytes for jfloat in C:\BUILD_AREA\jdk6_37\hotspot\src\share\vm\prims\jni.cpp
我怀疑是内存泄漏。
我是公司的新人,我想在 linux 上工作,但他们让我在 Windows 上工作:( 在生产中,我们使用 .so 文件,因为我们在 solaris 上,而我在 Windows 上使用 DLL(逻辑上)。
首先,我尝试重现生产问题。所以,我创建了一个单元测试来加载DLL并多次调用调用native方法的java类。 当我这样做时,我使用 processExplorer.exe 看到内存每 2 秒增长到 2MB。我有一个例外,就像在生产中一样。
我很高兴我成功地重现了这个问题,我可以说这个问题来自 C 或 Fortran 代码。
接下来,我尝试删除对 Fortran 的调用,而我的 java 只调用了 C(没有 Fortran,这个测试让我可以查看问题是来自 C 还是 Fortran。)
结果就是内存没动!凉爽的!我可以说我对 C 语言中的 malloc/free 没有任何问题。
所以,我决定学习一点 Fortran 来查看代码。 :)
我了解到,在 Fortran 中,我们可以使用 allocate 和 deallocate 关键字来处理内存。而且我的代码不包含这些关键字。 :(
在这一切之后,有人允许我在 Solaris 上启动我的 junit 测试,该测试调用 Java->JNI->C=>Fortran 并使用 .so 而不是 DLL。
还有惊喜 - 内存没有移动!!!我在 Solaris 或 RedHat 下没有任何问题。
我被卡住了,因为问题存在于生产中,但我无法清楚地重现它。 :(
为什么我发现 DLL 和 SO 之间存在差异?代码(java/C/Fortran)完全一样,因为是我编译的。
如何进行更多调查?
我尝试在重现问题的 Windows 下进行内存转储,但我什么也没看到。
是jvm的问题吗?还是问题出在通过 JNI 传递给 C 的对象上?
非常感谢您帮助我解决这个问题。
信息:我使用的是 Windows 7 64 位
PS:我是法国人,请原谅我的英语。我每次都尽力做到最好。 ;)
这是 C 代码的标题:
#ifndef unix
__MINGW_IMPORT void modlin_OM(float pmt[], float abaque[][], float don[][], float cond[], float res[][], int flag[]) ;
#else
extern void modlin_om_(float * pmt, float * abaque, float * don, float * cond, float * res, int * flag) ;
#endif
方法之后:
JNIEXPORT jint JNICALL Java_TrtModlin_modlin_1OM
(JNIEnv * env, jobject obj,
jfloatArray pmtPar,
jobjectArray abaquePar, jobjectArray donPar, jfloatArray condPar, jobjectArray resPar, jintArray flagPar)
一些代码,以及 Fortran 的方法调用
#ifndef unix
modlin_OM(pmt, abaque, don, cond, res, & iFlag) ;
#else
modlin_om_(pmt, abaque, don, cond, res, & iFlag) ;
#endif
正如我之前所说,我通过删除这些行来测试对 C 的调用并且内存没有增长 :( 我通过使用 free(someVar) 删除一行进行测试并且内存增长,因为在这种情况下没有完成 free . 这就是为什么我得出结论认为我的 C 可以使用 Free/Malloc。
【问题讨论】:
你有两个平台(Unix 和 Windows)的 C 绑定的源代码吗? 是的,我有源代码,但对于 windows 或 unix 是一样的,我在我的问题末尾添加它(如果你想要所有源代码,我可以给你) 哪个fortran编译器? 我们使用:FORTRANCOMPILER=f90 不多说了,哪个Fortran厂商?在 Solaris 上f90
将是 Sun/Oracle,但在 Windows 上它可以是任何东西。
【参考方案1】:
分析内存问题总是很复杂的。根据我的经验,有两种方法:
1) 你尝试复制。这假设您有源代码和根本原因的想法。 2)您观察生产崩溃:频率、与其他事件的相关性等。
这可以帮助确定它是否是内存泄漏(在业务负载下可能是高消耗......)
在您的特定情况下,我注意到以下几点:
代码的行为在不同的操作系统上可能不同。 Java 代码(JVM 错误)非常罕见。本机代码经常出现这种情况(例如,忘记关闭 ZIP 会导致 Linux 上的内存泄漏,但在 Windows 上不会...)
在 C 标头 (*.h) 中:abaque、don 和 res 在 Windows 上是“float * *”,在 Unix 上是“float *”。这可能是您的 C 标头中的错误,或者这意味着 C 实现不期望根据操作系统使用相同的参数类型(这对我来说很奇怪......)
在第二种情况下,您在 Windows(不是目标)上编译 C 头文件的事实可以解释您没有生成正确的 JNI 存根(典型的交叉编译问题)...从这里我们可以做很多假设,简单或非常复杂......
祝你好运!
【讨论】:
【参考方案2】:我在我的应用程序中遇到了同样的问题,但我提出了很好的解决方案。 请在使用 make null 之后创建 native 的对象,以便 gc 收集未引用的对象。
【讨论】:
欢迎,请尽量在每个句子的开头使用大写字母。以上是关于本机 C 和 Fortran 代码的 Java 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章
使用 GCC 对模块内的 fortran 子例程进行外部命名
使用 GCC 对模块内的 fortran 子例程进行外部命名