JNI库在垃圾回收时释放内存?

时间:2015-03-14 03:29:14

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

我正在使用JCUDA,想知道JNI对象是否足够聪明,可以在收集垃圾时解除分配?我可以理解为什么这可能在所有情况下都不起作用,但我知道它会在我的情况下起作用,所以我的后续问题是:我怎样才能做到这一点?是否有"模式"我可以设置?我需要构建一个抽象层吗?或许答案真的是"没有人不会尝试过#34;那么为什么不呢?

编辑:我只引用通过JNI创建的本机对象,而不是Java对象。我知道所有Java对象都被同等对待W.R.T.垃圾收集。

3 个答案:

答案 0 :(得分:2)

在JNI中创建的Java对象与所有其他Java对象相同,并且在它们到来时被垃圾收集和销毁。为了防止这些对象过早被破坏,我们经常使用JNI函数env->NewGlobalRef()(但它的用法绝不仅限于在本机中创建的对象)。

另一方面,本机对象不受垃圾回收的影响。

答案 1 :(得分:2)

这里有两种情况。

  1. 本机代码分配Java对象。这些对象与所有其他Java对象一样。如果本机出现并且拥有强大的引用,则可以阻止GC。
  2. 本机代码分配本机内存。 GC对此一无所知;它是由图书馆安排释放它。一种方法是使Java对象具有终结器,该终结器对必要的JNI调用释放本机内存。

答案 2 :(得分:1)

通常,由于垃圾回收,此类库不会释放内存。特别是:JCuda没有这样做,并且没有选项或“模式”可以做到这一点。

原因很简单:它不起作用。

你经常会有这样的模式:

void doSomethingWithJCuda()
{
    CUdeviceptr data = new CUdeviceptr();
    cuMemAlloc(data, 1000);

    workWith(data);

    // *(See notes below)
}

这里,分配了本机内存,Java对象充当此本机内存的“句柄”。

在最后一行,data对象超出范围。因此,它有资格进行垃圾收集。但是,有两个问题:


1。垃圾收集器销毁Java对象,而释放用cuMemAlloc分配的内存或任何其他本地电话。

因此,您通常必须通过显式调用

来释放本机内存
cuMemFree(data);

离开方法之前。


2。您不知道Java对象何时会被垃圾收集 - 或者它是否会被垃圾收集。

一个常见的误解是当一个对象不再可达时会变成垃圾收集,但这不一定是真的。

正如bmargulies在他的回答中指出的那样:

  

一种方法是让一个带有终结器的Java对象进行必要的JNI调用以释放本机内存。

简单地覆盖这些“句柄”对象的finalize()方法可能看起来像一个可行的选项,并在那里进行cuMemFree(this)调用。例如,JavaCL的作者(一种也允许将GPU与Java一起使用的库,因此在概念上有点类似于JCuda)已经尝试过这种方法。

但它根本不起作用:即使Java对象不再可访问,意味着它将立即被垃圾收集。

您根本不知道何时调用finalize()方法。

这很容易造成令人讨厌的错误:当你有100 MB的GPU内存时,你可以使用10个CUdeviceptr个对象,每个对象分配10MB。你的GPU内存已满。但对于Java,这几个CUdeviceptr对象只占用几个字节,并且在应用程序运行时期间可能根本不会调用finalize()方法,因为JVM根本不需要 / em>回收这几个字节的内存。 (在这里省略关于hacky变通方法的讨论,比如调用System.gc()左右 - 底线是:它不起作用。)


回答你的实际问题:JCuda是一个非常低级的库。这意味着您拥有全部功能,但也有手动内存管理的全部职责。我知道这是“不方便的”。当我开始创建JCuda时,我最初打算将它作为面向对象的包装器库的低级后端。但是为复杂的通用库(如CUDA)创建健壮,稳定且普遍适用的抽象层是一项挑战,我不敢解决这样一个项目 - 最后但并非最不重要的是因为复杂性被垃圾收集等事物暗示......