JNI - 来自 C++ 的免费 ByteBuffer

Posted

技术标签:

【中文标题】JNI - 来自 C++ 的免费 ByteBuffer【英文标题】:JNI - Free ByteBuffer from C++ 【发布时间】:2013-02-07 12:49:01 【问题描述】:

总结

    使用 ByteBuffer.allocateDirect(someBufferSize) 在 Java 中创建名为 buffer 的 ByteBuffer 用数据填充缓冲区 将缓冲区作为作业对象传递给 C++ - jbuffer 使用 env->GetDirectBufferAddress(jbuffer) 获取缓冲区直接指针 在 C++ 端处理缓冲区数据。如何防止 GC 清理我们的缓冲区,否则它永远不会发生? 工作完成 - 我们现在不需要 jbuffer。 释放 jbuffer? free(jbuffer) - 将引发无效地址错误

长部分

我使用下一个代码通过 Java AssetManager 加载 PNG 文件,以使用它们创建 Open GL ES 2.0 纹理。

Java 端 PNG 类

import java.nio.ByteBuffer;
import android.graphics.Bitmap;

public class PNG 

 private static final int BYTES_PER_PIXEL_PNG = 4;
 private static final String LOG_TAG = "[PNG]";
 public int width;
 public int height;
 public ByteBuffer pixels;


 public PNG(Bitmap bitmap)
 
  this.width = bitmap.getWidth();
  this.height = bitmap.getHeight();
  this.pixels = ByteBuffer.allocateDirect(this.width * this.height * BYTES_PER_PIXEL_PNG);
  bitmap.copyPixelsToBuffer(this.pixels);
 


public static PNG loadPNG(String path)

    InputStream is = null;
    try
    
        is = ASSETS_MANAGER.open(path);//get png file stream with AssetsManager instance
    
    catch (IOException e)
    
        Log.e(LOG_TAG, "Can't load png - " + path, e);
    

    return new PNG(BitmapFactory.decodeStream(is));

C++ 端 PNG

typedef struct png

int width;
int height;
char* pixels;
 png;

png* load_png(const char* path)

 png* res = (res*) malloc(sizeof(png);
 ...
 jobject _png = env->CallStaticObjectMethod(get_java_lib_class(), get_method_id(JAVA_LIB_LOAD_PNG, JAVA_LIB_LOAD_PNG_SIGN), _path);//Calling loadPng() from Java, get PNG jobject
 jobject _pixels =  env->GetObjectField(_png, PNG_FIELDS->PNG_PIXELS_ID);//Getting pixels field from Java PNG jobject
 res->pixels = (char*) env->GetDirectBufferAddress(_pixels);//Get direct pointer to our pixel data
 //Cleanup
 ...
 env->DeleteLocalRef(_png);
 env->DeleteLocalRef(_pixels);
 return res;

然后使用png来创建纹理

void test_create_tex(const char* path)

 ...
 png* source = load_png(path);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, source->width, source->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, source->pixels);
//We don't need source pixel data any more
//free(source->pixels);//INVALID HEAP ADDRESS (deadbaad)
free(source);

那么在 C++ 端使用它的直接指针后如何释放字节缓冲区?它是直接分配的(如 malloc - 在本机端)并且必须被释放,否则我会得到 OutOfMemory 错误。

【问题讨论】:

与***.com/questions/1246983/…密切相关 【参考方案1】:

您不需要释放缓冲区。您已经在 J​​ava 端分配了它,这意味着它是 JVM 对象,GC 会处理它。与在 C 端分配相反,因此是 GC 不知道的本机对象。您甚至不需要执行DeleteLocalRef,因为在从本机方法返回时,JNI 机器将为您删除所有本地引用。只有在一个本机调用范围内有数百个 JNI 调用返回 JVM 时,您才需要显式删除,因此即使在返回 JVM 之前,您也会用完句柄。

我必须承认,我不知道 GC 是如何知道它不应该触及您的 ByteBuffer,但我猜想通过调用 GetObjectField,您会增加 ByteBuffer 上的引用计数并减少 DeleteLocalRef。所以在这两个 JNI 调用之间,ByteBuffer 是安全的。

【讨论】:

其实有一部分是错误的。当您创建一个直接 NIO 缓冲区时,它的一小部分分配在垃圾收集器管理的 Java 堆上,而包含您的数据的部分则分配在本机堆上。当没有足够的本地内存来创建新的直接 NIO 缓冲区时,有一个非常弱的机制会尝试请求垃圾收集器,但最好考虑到本地内存的管理取决于开发人员。我建议你阅读这份文件:ibm.com/developerworks/library/j-nativememory-linux/index.html【参考方案2】:

在我看来,您不必担心ByteBuffer pixels 的释放,因为它是由JVM 管理的。您真正应该关心的是在 C++ 使用它时防止它被垃圾收集。

【讨论】:

以上是关于JNI - 来自 C++ 的免费 ByteBuffer的主要内容,如果未能解决你的问题,请参考以下文章

Java项目采用JNI方式加载Quantlib c++(SWIG)库

Java项目采用JNI方式加载Quantlib c++(SWIG)库

Android在C ++ JNI代码中从另一个活动类调用Java函数

一步一步学习JNI

一步一步学习JNI

Android JNI之JAVA与C++对象建立对称关联(JNI优化设计,确保JNI调用的稳定性)