JNI 開發中 Java String 傳遞至 C/C++ 是高頻操作。本文詳細說明 jstring 轉 char* 的標準流程、內存管理機制及常見錯誤,確保無內存洩漏、無崩潰。
| JNI 函數 | 作用 | 關鍵參數 |
|---|---|---|
GetStringUTFChars | 將 jstring 轉為 UTF-8 編碼的 C 字串 | (JNIEnv*, jstring, jboolean* isCopy) |
ReleaseStringUTFChars | 釋放 GetStringUTFChars 申請的內存/固定 | (JNIEnv*, jstring, const char*) |
GetStringLength | 獲取 Unicode 字符數 (非字節數) | (JNIEnv*, jstring) |
GetStringUTFLength | 獲取 UTF-8 編碼字節數 | (JNIEnv*, jstring) |
NewStringUTF | 從 UTF-8 C 字串創建新 jstring | (JNIEnv*, const char*) |
JNIEXPORT void JNICALL
Java_HelloWorld_displayHelloWorld(JNIEnv *env, jobject obj, jstring s) {
// 1. 空指針防護
if (s == NULL) return;
// 2. 獲取 UTF-8 C 字串指針
// isCopy 為 NULL 表示不關心是否拷貝
const char *str = (*env)->GetStringUTFChars(env, s, NULL);
if (str == NULL) return; // OOM 保護
// 3. 業務邏輯使用 str
printf("Received from Java: %s\n", str);
// 可在此處調用其他 C 函數處理字串
// 4. **必須**釋放資源,配對調用
(*env)->ReleaseStringUTFChars(env, s, str);
}
GetStringUTFChars 可能:固定對象 (pinning) 防止 GC 移動,或 拷貝 到 C 堆jboolean isCopy;
const char *str = (*env)->GetStringUTFChars(env, s, &isCopy);
if (isCopy == JNI_TRUE) {
// JVM 拷貝了字串,str 指向 C 堆內存
// Release 會 free 這塊內存
} else {
// JVM 固定了 Java 對象,str 直接指向堆內部
// Release 會解除固定
}
無論 isCopy 真假,**Release 邏輯統一**,無需分支處理。
| 錯誤代碼 | 後果 | 修正 |
|---|---|---|
| 漏調 Release | 內存洩漏 / GC 卡死 / JVM 崩潰 | try-finally 模式保證釋放 |
| Release 後繼續用 str | Use-after-free,未定義行為 | Release 即為 str 生命週期終點 |
| 傳錯 jstring 給 Release | 釋放錯誤內存,堆損壞 | 原樣傳入 GetStringUTFChars 返回的 jstring |
| 多線程共用 str | 線程不安全,JNIEnv 非線程安全 | 每線程獨立 Get/Release |
class ScopedUtfChars {
public:
ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), str_(s) {
chars_ = env_->GetStringUTFChars(str_, NULL);
}
~ScopedUtfChars() {
if (chars_) env_->ReleaseStringUTFChars(str_, chars_);
}
const char* get() const { return chars_; }
private:
JNIEnv* env_;
jstring str_;
const char* chars_;
};
// 使用示例
JNIEXPORT void JNICALL Java_HelloWorld_display(JNIEnv* env, jobject obj, jstring s) {
ScopedUtfChars scoped(env, s);
const char* str = scoped.get();
if (str) printf("%s\n", str);
// 離開作用域自動 Release,異常安全
}
GetStringUTFChars 返回 **Modified UTF-8**,補文字符 (U+0000) 編碼為 0xC0 0x80 而非單字節 0x00strlen, strcmp) 可能誤判長度/內容GetStringChars 獲取 UTF-16 jchar* 配合 ICU 庫GetStringUTFChars / ReleaseStringUTFChars 必須成對調用,作用域內使用,異常安全封裝。理解 JVM 固定/拷貝雙模式,避開編碼陷阱,即可編寫健壯的 JNI 字串交互代碼。