JavaCPP Leptonica:如何清除pixClone句柄的内存

时间:2019-11-12 07:22:52

标签: leptonica javacpp

直到现在,我一直使用pixDestroy来清理JavaCPP / Leptonica应用程序中的PIX对象。但是,我最近注意到了一个奇怪的内存泄漏问题,该问题一直追溯到内部返回pixClone结果的Leptonica函数。我设法通过使用以下简单测试来重现该问题:

    @Test
    public void test() throws InterruptedException {
        String pathImg = "...";

        for (int i = 0; i < 100; i++) {
            PIX img   = pixRead(pathImg);
            PIX clone = pixClone(img);

            pixDestroy(clone);
            pixDestroy(img);
        }

        Thread.sleep(10000);
    }

到达Thread.sleep时,Windows任务管理器中的RAM内存使用量(不是堆大小)已增加到大约1GB,并且直到睡眠结束并且测试完成才释放。

看看pixClone的文档,我们看到它实际上为现有PIX创建了一个句柄:

  

注意:

     
      
  1. “克隆”只是现有像素的句柄(ptr)。这样做是因为(a)图像可能很大,因此图像昂贵   复制,以及(b)数据结构的额外句柄需要使用   避免双重释放和内存泄漏的简单策略。像素是   参考计数。 pixClone()的副作用增加了1   在引用计数中。

  2.   
  3. 要使用的协议是:(a)每当您想要现有图像的新句柄时,请调用pixClone(),它只会增加引用计数。 (b)   始终在所有句柄上调用pixDestroy()。这会减少参考   count,使句柄为空,并且仅在pixDestroy()时销毁pix   已在所有句柄上被调用。

  4.   

如果我正确理解这一点,那么我确实在所有句柄上都调用pixDestroy,因此引用计数应该达到零,因此PIX应该已经被销毁。显然,事实并非如此。有人可以告诉我我在做什么错吗?预先感谢!

1 个答案:

答案 0 :(得分:1)

作为对函数返回作为参数接收的指针的常见情况的优化,JavaCPP还将相同的对象返回给JVM。 pixClone()就是这种情况。它只是返回用户作为参数传递的指针,因此imgclone最终都引用Java中的同一对象。

现在,当在第一个引用pixDestroy()上调用img时,Leptonica会很有帮助地将其地址重置为0,但现在我们丢失了地址,而第二次调用了pixDestroy()接收到该空指针,从而导致noop和内存泄漏。

避免此问题的一种简单方法是在每次调用PIX之后显式创建一个新的pixClone()引用,例如,在这种情况下:

PIX clone = new PIX(pixClone(img));