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】:您不需要释放缓冲区。您已经在 Java 端分配了它,这意味着它是 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)库