通过 java API 并使用 malloc 创建缓冲区将字符串处理到 JNI 层
Processing a string into JNI layer by passing through java API and creating a buffer using malloc
我正在研究 java API,它将用于处理 cpp 中的字符串和 return 已处理的字符串。
我使用的jni中的cpp代码如下所述
char * buffer;
static
void process(char *str, int shift) {
// some processing
}
extern "C" JNIEXPORT jstring JNICALL Java_com_example_process (
JNIEnv *env,
jobject,
jstring string,
jint shift) {
const char *key = env->GetStringUTFChars(string, nullptr);
buffer = (char *)malloc(sizeof(key));
strcpy(buffer, key);
char *p = buffer;
process(buffer, shift);
env->ReleaseStringUTFChars(string, key);
return env->NewStringUTF(p);
}
extern "C" JNIEXPORT void JNICALL Java_com_example_freeN (
JNIEnv *env,
jobject) {
free(buffer);
buffer = nullptr;
}
Java API
fun processStr(string: String): String {
val str = process(string, 10)
freeN()
return str
}
这里有几点需要注意:
1. 由于使用 JNI 从 java 传递给 C 的字符串,它作为 const 接收,我无法进行任何操作,因此创建并使用缓冲区strcpy 复制然后操作字符串
2. 从 java 代码本身调用 freeN() 以释放分配的缓冲区。
3. 可能还有另一种方法,从 java 传递 java 的 ByteBuffer,然后在 JNI 中使用缓冲区 这种方式不会释放内存有问题吗?
这些是解决这个问题的正确方法吗?有什么更好的方法?我怎样才能使它线程安全?
你认为异步调用 java API 是个好主意还是 API 应该同步?
我无法从你的问题中弄清楚 process
是否依赖于它的输入以 null 终止,但请注意,这不是规范所要求的。我也不知道 process
' 输入和输出的形状应该是什么。如果你要传入或传出任意数据,那应该是 Java byte[]
或 ByteBuffer
, 而不是 java.lang.String .
env->NewStringUTF(p)
分配一个新的 Java 字符串,其内存由 Java.
管理
一旦分配了字符串,您就可以安全地释放 buffer
.
其次,通过全局存储 buffer
,您使您的函数不可重入,并且让您的生活更加艰难。想象一下如果两个线程同时调用 Java_com_example_process
会发生什么。
最后,sizeof(key)
returns 指针 的大小,而不是它指向的东西。因此,如果您的字符串超过 4 或 8 个字节(取决于体系结构),这将严重失败。幸运的是,您可以简单地询问 JNI 您的字符串长度是多少。
我建议进行以下重写,其中 JVM 负责处理返回的字符串,因此您不必再调用 freeN
。
static void process(const char *in, char *out, int shift) {
// some processing where you take data from `in` and write to `out`
}
extern "C" JNIEXPORT jstring JNICALL Java_com_example_process (
JNIEnv *env,
jobject,
jstring string,
jint shift) {
jsize key_size = env->GetStringUTFLength(string);
const char *key = env->GetStringUTFChars(string, nullptr);
char * buffer = new char[key_size];
process(key, buffer, shift);
env->ReleaseStringUTFChars(string, key);
jstring ret = env->NewStringUTF(buffer);
delete[] buffer;
return ret;
}
我正在研究 java API,它将用于处理 cpp 中的字符串和 return 已处理的字符串。
我使用的jni中的cpp代码如下所述
char * buffer;
static
void process(char *str, int shift) {
// some processing
}
extern "C" JNIEXPORT jstring JNICALL Java_com_example_process (
JNIEnv *env,
jobject,
jstring string,
jint shift) {
const char *key = env->GetStringUTFChars(string, nullptr);
buffer = (char *)malloc(sizeof(key));
strcpy(buffer, key);
char *p = buffer;
process(buffer, shift);
env->ReleaseStringUTFChars(string, key);
return env->NewStringUTF(p);
}
extern "C" JNIEXPORT void JNICALL Java_com_example_freeN (
JNIEnv *env,
jobject) {
free(buffer);
buffer = nullptr;
}
Java API
fun processStr(string: String): String {
val str = process(string, 10)
freeN()
return str
}
这里有几点需要注意:
1. 由于使用 JNI 从 java 传递给 C 的字符串,它作为 const 接收,我无法进行任何操作,因此创建并使用缓冲区strcpy 复制然后操作字符串
2. 从 java 代码本身调用 freeN() 以释放分配的缓冲区。
3. 可能还有另一种方法,从 java 传递 java 的 ByteBuffer,然后在 JNI 中使用缓冲区 这种方式不会释放内存有问题吗?
这些是解决这个问题的正确方法吗?有什么更好的方法?我怎样才能使它线程安全? 你认为异步调用 java API 是个好主意还是 API 应该同步?
我无法从你的问题中弄清楚 process
是否依赖于它的输入以 null 终止,但请注意,这不是规范所要求的。我也不知道 process
' 输入和输出的形状应该是什么。如果你要传入或传出任意数据,那应该是 Java byte[]
或 ByteBuffer
, 而不是 java.lang.String .
env->NewStringUTF(p)
分配一个新的 Java 字符串,其内存由 Java.
管理
一旦分配了字符串,您就可以安全地释放 buffer
.
其次,通过全局存储 buffer
,您使您的函数不可重入,并且让您的生活更加艰难。想象一下如果两个线程同时调用 Java_com_example_process
会发生什么。
最后,sizeof(key)
returns 指针 的大小,而不是它指向的东西。因此,如果您的字符串超过 4 或 8 个字节(取决于体系结构),这将严重失败。幸运的是,您可以简单地询问 JNI 您的字符串长度是多少。
我建议进行以下重写,其中 JVM 负责处理返回的字符串,因此您不必再调用 freeN
。
static void process(const char *in, char *out, int shift) {
// some processing where you take data from `in` and write to `out`
}
extern "C" JNIEXPORT jstring JNICALL Java_com_example_process (
JNIEnv *env,
jobject,
jstring string,
jint shift) {
jsize key_size = env->GetStringUTFLength(string);
const char *key = env->GetStringUTFChars(string, nullptr);
char * buffer = new char[key_size];
process(key, buffer, shift);
env->ReleaseStringUTFChars(string, key);
jstring ret = env->NewStringUTF(buffer);
delete[] buffer;
return ret;
}