【问题标题】:Garbage collection and JNI call垃圾收集和 JNI 调用
【发布时间】:2016-12-05 11:40:59
【问题描述】:

我遇到了一个 JNI 程序随机耗尽内存的问题。

这是一个 32 位 java 程序,它读取文件,进行一些图像处理,通常使用 250MB 到 1GB。然后丢弃所有这些对象,然后程序对通常需要 100-250MB 的 JNI 程序进行一系列调用。

当交互式运行时,我从未见过问题。但是,当对多个文件连续执行批处理操作时,JNI 程序将随机耗尽内存。它可能有一个或两个文件的内存问题,然后在接下来的 10 个文件中运行良好,然后再次出现故障。

我已经在 J​​NI 调用之前转储了可用内存量,它遍布整个地图,有时是 100MB,有时是 800MB。我的解释是,Java 垃圾收集有时会在图像处理后立即运行,有时则不会。如果不是,则可能没有足够的内存供 JNI 程序使用。

我已经阅读了所有关于 GC 是不确定的、不应该调用它、不会产生任何影响等的内容,但似乎在开始 JNI 调用之前强制 GC 会改善这种情况。

但是有什么方法可以真正确保在继续之前有一定数量的空闲内存?

要回答有关 JNI 程序的问题,该程序由另一家公司提供,我对它如何分配内存没有真正的了解。我所知道的是它是用 c++ 编写的,它没有垃圾收集。有人告诉我它需要 100-250MB 的内存,而我看到的数字可以证实这一点。

也许我应该将问题改写为:如果我要进行一个我知道需要 250MB 内存的 JNI 调用,我如何确保它有那么多可用内存?

确实,一种可能的解决方案是进行 64 位构建。但是,这个批处理操作是 32 位构建的 QA 的一部分,所以我想测试真实的东西。

【问题讨论】:

  • 您没有向我们展示任何代码。这就像让您的医生通过电话诊断您的间歇性疼痛,甚至没有告诉她疼痛在哪里。请访问help center 并阅读How to Ask
  • 增加最大堆大小是一种选择吗?
  • @JimGarrison 不一定。他很好地描述了这个问题,以至于代码在一般情况下不会带来太多(即如果他没有在他的代码中做疯狂的事情)。这是关于 JNI 调用期间 GC 和内存使用的一般问题。我无法回答,但我仍然认为这个问题很好
  • 我认为至少需要更多关于 JNI 代码在做什么的细节,尤其是它如何分配内存。 GC 是一个非常困难的话题,具体案例的信息越多越好。
  • 问:“可用内存量”到底是什么意思?问:究竟什么是“内存不足”——JRE,还是你的操作系统? Java堆,还是“别的东西”?问:您的操作系统是什么?您的 JRE 版本?

标签: java garbage-collection java-native-interface


【解决方案1】:

我自己解决这个问题的方法是简单地调用System.gc(),但从内部本机代码:

#include <jni.h>
// ...
int my_native_function(JNIEnv* env, jobject obj) {
    jclass    systemClass    = nullptr;
    jmethodID systemGCMethod = nullptr;
    // ...
    // Take out the trash.
    systemClass    = env->FindClass("java/lang/System");
    systemGCMethod = env->GetStaticMethodID(systemClass, "gc", "()V");
    env->CallStaticVoidMethod(systemClass, systemGCMethod);
}

我希望这也适用于你。

【讨论】:

    【解决方案2】:

    FWIW(我意识到这是一种异端邪说)添加对

    的调用
    System.gc();
    

    在对每个文件进行第一次 JNI 调用之前,这种情况得到了显着改善。而不是在 20% 的文件上出现内存错误,它现在不到 5%。更好的是,错误不再是随机的,而是可以在运行中重复,因此可以推测它们可以被追踪。

    【讨论】:

      【解决方案3】:

      以下假设您使用的是热点 jvm。

      32 位进程不仅受限于提交的内存,更重要的是它们受限于虚拟内存,即保留的地址空间。在 64 位系统上,您只有 4GB 的可用地址,在 32 位系统上只有 2-3GB。

      JVM 将预先为托管堆保留一个固定的、可能大量的地址空间,然后在该数量之上动态分配一些内部结构,然后可能为 DirectByteBuffers 或内存映射文件分配更多。这会给原生代码留下非常小的运行空间。

      使用Native Memory Tracking 确定JVM 的各个部分正在使用多少,使用pmap &lt;pid&gt; 检查内存映射文件。然后尝试在不妨碍您的应用程序的情况下限制它。

      或者,您可以生成一个新进程并在那里进行图像处理。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-03-21
        • 2013-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多