【问题标题】:Is there any way that I can free up memory in the java code that is generated to bind C code via JNI/JNA?有什么方法可以释放生成的 java 代码中的内存以通过 JNI/JNA 绑定 C 代码?
【发布时间】:2016-11-15 04:28:22
【问题描述】:

我正在使用一个使用 JNA 绑定到原始 C 库(该库称为 Leptonica)的 java 库。我遇到了必须在 C 代码中调用 free(data) 以释放内存的情况。但是,java中有什么函数可以释放内存吗?

在 C 代码中

void ImageData::SetPixInternal(Pix* pix, GenericVector<char>* image_data) {
  l_uint8* data;
  size_t size;
  pixWriteMem(&data, &size, pix, IFF_PNG);
  pixDestroy(&pix);
  image_data->init_to_size(size, 0);
  memcpy(&(*image_data)[0], data, size);
  free(data);
}

函数 pixWriteMem() 将创建并为“数据”分配内存,您需要稍后执行 free(data) 以释放内存。

在 Java 代码中,我只能访问 pixWriteMem(),不能访问 SetPixInternal(),所以我无法释放“数据”,这会造成内存泄漏。

【问题讨论】:

  • 除非你有特定的对象可以清除,否则,你需要等待垃圾收集器为你清理。
  • 其他人都在谈论垃圾收集器和 Java 的内存。请注意,OP 正在谈论在 Java 中调用的 C 代码,请注意 JNI 标记。这不像让垃圾收集器清理那么简单。
  • 您的c代码与实物完全不同,请正确剪切/粘贴。
  • 我同意@KevinDTimm。我认为这个问题比我们意识到的要多。
  • @KevinDTimm ,Aaron,是的,我将编辑 C 代码。由于原始代码涉及多个功能,所以我让它变得非常简单。我会改写它。是的,这个问题并不像让垃圾收集器清理那么简单。我已经尝试过 system.gc() 但它不工作

标签: java c memory-leaks java-native-interface jna


【解决方案1】:

不,Java 中没有像 C 的 free() 这样的函数。但是你可以通过调用 System.gc() 来建议垃圾收集器运行

【讨论】:

  • 为 JNI 内存运行 GC 没有用处,除非该内存由 Java 对象使用 finalize 方法管理,该方法调用本机 C 代码以释放内存。这在一定程度上是可行的,并且会受到finalize 附带的警告的约束,但它是最终确定的标准用例之一。
  • 好的,我误解了这个问题。对于 JNI/JNA,我的评论无效。
【解决方案2】:

这里的其他 cmets 和答案似乎都在建议您只依赖垃圾收集器或告诉垃圾收集器运行。这不是在 C 中分配内存并通过 JNI 在 Java 中使用的正确答案。

看起来execution() 确实释放了内存。你给我们看的最后一行是free(data)。不过,要按照您的要求回答您的问题,答案是“不直接”。如果您有能力添加到 C 代码中,您可以创建另一个释放数据的 C 函数,然后使用 JNI 调用它。也许还有更多我们没有看到的与您对内存泄漏的担忧更相关的内容?

此外,请注意释放您正在使用的库分配的内存。在尝试释放它之前,您应该确保该库不再需要它并且正在泄漏它。

现在回到一般的内存管理...

Java 确实是一种垃圾收集语言。这意味着您不会专门删除对象。相反,您确保没有对它的引用,然后垃圾收集器负责内存管理。这并不意味着 Java 没有内存泄漏,因为有一些方法会意外地保留一个引用,从而使对象永远不会被垃圾回收。如果您遇到这种情况,您可能需要阅读the different kinds of references in Java (strong/weak/etc.)

同样,这不是这里的问题。这是一个 C/Java 混合体,有问题的代码在 C 中被 Java 调用。在 C 中,您分配要使用的内存,然后您需要在完成后自己 free 内存。即使 C 代码是由 Java 通过 JNI 运行的,您仍然需要为自己的内存负责。您不能只malloc() 一堆内存,并期望Java 垃圾收集器知道何时清理它。因此OP的问题。

如果您需要自己添加功能来执行free,即使没有 C 部分的源代码,如果您可以访问指针,您仍然可以编写自己的 C 接口来释放内存到有问题的内存。您基本上可以编写一个小型库,为您释放内存,为其创建 JNI 接口,并将指针传递给它。如果你走这条路,那么根据你的操作系统,你可能需要保证你的小型免费库的本机代码与其他本机代码在同一进程中运行,或者如果不是同一进程,那么至少该进程您运行它时对其他代码进程拥有的内存具有写访问权限;在您的情况下,此内存/进程问题可能不是问题,但为了完整起见,我将其丢弃。

【讨论】:

  • Downvoter:如果我说错了什么,请告诉我是什么,以便我修复它。我不想传播错误信息。
  • 谢谢。嗯,好像除了改C代码没办法?
  • 如果它是在 C 中分配的(如 malloc() 或等价物),那么是的,它最终需要以某种方式在 C 中释放。即使你想从 Java 中管理它,您仍然需要在 C 中做一些事情,然后将该 C 功能公开给 Java 以通过 JNI 调用。
  • 我应该详细说明一下:从技术上讲,Java 甚至不知道何时需要释放 C 分配的内存。在 C 中,有可能(很容易?)以这样一种方式编写代码,即没有垃圾收集器知道何时释放它。 int* myPointer; void initN() { myPointer = malloc(sizeof(int*)); }你怎么知道什么时候释放它?即使调用它的 Java 对象被 GCed,其他一些 Java 对象也可能通过 JNI 使用 myPointer 并期望它仍然存在。你永远不知道,Java 也不知道,所以 C 程序员需要处理内存;但可能只是免费向 Java 公开。
  • 谢谢。我想解决它的唯一方法是让C开发添加该函数,然后Java JNI可以调用它来释放内存。
【解决方案3】:

在 Java 代码中,我只能访问 createData(),而不能访问 excution(),所以我无法释放“数据”,这会造成内存泄漏。

那么做你就糟透了。

说真的,如果您想释放由本机方法分配的内存并且在该方法返回之前未释放,那么您需要在该内存上维护某种句柄,然后将其传递给另一个将释放内存的本机方法.如果您目前没有这样的本地方法可用,那么您需要创建一个。

另一个问题是如何确保调用所需的本机方法。依赖用户直接或间接调用它,如果用户不这样做,您就会面临内存泄漏。解决这个问题有两种主要方法:

  • 给你的类一个终结器,确保内存被释放。这是终结器的核心用例,但即便如此,还是有充分的理由避免编写它们。另一种选择是

  • 创建一个引用对象(SoftReferenceWeakReferencePhantomReference),将引用与释放属于所引用 Java 对象的本机分配内存的机制相关联(但不是 通过该对象),并将该对象注册到引用队列中。当对象被 GC 时,引用将被加入队列,此时您知道要释放本机分配的内存。

这并不一定意味着您应该阻止用户显式释放内存,因为通过足够的簿记,您可以跟踪在任何给定时间是否仍需要释放任何内容。允许用户显式释放资源可能有助于降低整体资源使用率。但是如果你想避免内存泄漏,那么你需要有一个回退。

【讨论】:

  • 谢谢约翰。实际上,我不确定我是否理解您的建议。你能详细说明一下吗?谢谢。
  • @RockTheStar,您需要拥有或创建一个 Java 类,其实例负责本地分配的内存。他们需要保留指向已分配块的指针的副本,可能转换为 Java longs。每次执行本机分配时,您都需要创建一个适当初始化的实例。假设您选择第一个选项,它更容易编写,您将为该类提供一个负责释放内存的finalize() 方法。您可能需要编写一个新的本机方法以用于该目的。
  • 好的。我想这不是一个容易解决的问题。
猜你喜欢
  • 2014-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多