垃圾收集时JNI库释放内存? [英] JNI libraries deallocate memory upon garbage collection?

查看:274
本文介绍了垃圾收集时JNI库释放内存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用JCUDA,想知道JNI对象是否足够聪明以便在垃圾收集时释放它们?我可以理解为什么这可能不适用于所有情况,但我知道它适用于我的情况,所以我的后续问题是:我该如何实现?有没有可以设置的模式?我需要建立一个抽象层吗?或者,答案可能是不要尝试,那么为什么不呢?

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

解决方案

通常情况下,这些库不会因垃圾收集而释放内存。特别是:JCuda不这样做,并且没有选择或模式,在这可以完成。

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



您经常会有这样的模式:

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

workWith(data);

// *(见下面的注释)
}

在这里,本地内存被分配,并且Java对象充当这个本地内存的句柄。

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




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



所以你通常必须释放本地内存,通过显式调用

  cuMemFree(数据); 

在离开方法之前。




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



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



由于 bmargulies 在他的回答中指出:


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


它可能看起来像是一个可行的选项,可以简单地覆盖这些句柄对象的 finalize()方法,在那里做 cuMemFree(this)调用。例如,JavaCL的作者(一个也允许在Java中使用GPU的库,因此在概念上与JCuda有些相似)已经尝试了这种方法。 但是它根本行不通:即使一个Java对象不再可用,这个不是意味着它将立即被垃圾回收。

您只是不知道何时调用 finalize()方法。

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




因此,回答您的实际问题: JCuda 是一个非常低级的图书馆。这意味着您拥有全部的权力,但也是手动内存管理的全部责任。我知道这是不方便的。当我开始创建JCuda时,我最初打算将它作为面向对象包装库的低级后端。但是为像CUDA这样的复杂通用库创建一个强大,稳定和普遍适用的抽象层是具有挑战性的,我不敢去处理这样一个项目 - 最后但并非最不重要的原因是复杂性隐含着......垃圾收集等事情......


I am using JCUDA and would like to know if the JNI objects are smart enough to deallocate when they are garbage collected? I can understand why this may not work in all situations, but I know it will work in my situation, so my followup question is: how can I accomplish this? Is there a "mode" I can set? Will I need to build a layer of abstraction? Or maybe the answer really is "no don't ever try that" so then why not?

EDIT: I'm referring only to native objects created via JNI, not Java objects. I am aware that all Java objects are treated equally W.R.T. garbage collection.

解决方案

Usually, such libraries do not deallocate memory due to garbage collection. Particularly: JCuda does not do this, and has no option or "mode" where this can be done.

The reason is quite simple: It does not work.

You'll often have a pattern like this:

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

    workWith(data);

    // *(See notes below)
}

Here, native memory is allocated, and the Java object serves as a "handle" to this native memory.

At the last line, the data object goes out of scope. Thus, it becomes eligible for garbage collection. However, there are two issues:


1. The garbage collector will only destroy the Java object, and not free the memory that was allocated with cuMemAlloc or any other native call.

So you'll usually have to free the native memory, by explicitly calling

cuMemFree(data);

before leaving the method.


2. You don't know when the Java object will be garbage collected - or whether it will be garbage collected at all.

A common misconception is that an object becomes garbage collected when it is no longer reachable, but this is not necessarily true.

As bmargulies pointed out in his answer:

One means is to have a Java object with a finalizer that makes the necessary JNI call to free native memory.

It may look like a viable option to simply override the finalize() method of these "handle" objects, and do the cuMemFree(this) call there. This has been tried, for example, by the authors of JavaCL (a library that also allows using the GPU with Java, and thus, is conceptually somewhat similar to JCuda).

But it simply does not work: Even if a Java object is no longer reachable, this does not mean that it will be garbage collected immediately.

You simply don't know when the finalize() method will be called.

This can easily cause nasty errors: When you have 100 MB of GPU memory, you can use 10 CUdeviceptr objects, each allocating 10MB. Your GPU memory is full. But for Java, these few CUdeviceptr objects only occupy a few bytes, and the finalize() method may not be called at all during the runtime of the application, because the JVM simply does not need to reclaim these few bytes of memory. (Omitting discussions about hacky workarounds here, like calling System.gc() or so - the bottom line is: It does not work).


So answering your actual question: JCuda is a very low-level library. This means that you have the full power, but also the full responsibilities of manual memory management. I know that this is "inconvenient". When I started creating JCuda, I originally intended it as a low-level backend for an object-oriented wrapper library. But creating a robust, stable and universally applicable abstraction layer for a complex general-purpose library like CUDA is challenging, and I did not dare to tackle such a project - last but not least because of the complexities that are implied by ... things like garbage collection...

这篇关于垃圾收集时JNI库释放内存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆