首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

解決 Cygwin 環境下 JNI 加載死鎖:自製 JVM 啟動器實戰

     編輯:Java 相關知識

問題背景

在 Windows 上使用 Cygwin 編譯的 JNI 動態庫(依賴 cygwin1.dll),直接用標準 java 或 javaw.exe 啟動 JVM 時,System.loadLibrary() 會導致進程死鎖。根因:Cygwin DLL 需要 Cygwin 進程上下文才能正確初始化,而標準啟動器不是 Cygwin 進程。

解決思路

編寫一個 Cygwin 原生 C 程序 作為 JVM 啟動器,利用 JNI Invocation API 創建 JVM。因為啟動器本身在 Cygwin 環境下編譯鏈接,擁有完整的 Cygwin 運行時上下文,後續加載依賴 cygwin1.dll 的 JNI 庫即可正常工作。

核心技術點

1. JNI_CreateJavaVM 關鍵結構體

typedef struct JavaVMInitArgs {
    jint version;           // 必須 JNI_VERSION_1_2 或以上
    jint nOptions;
    JavaVMOption *options;  // JVM 選項陣列
    jboolean ignoreUnrecognized;
} JavaVMInitArgs;

typedef struct JavaVMOption {
    char *optionString;     // 如 "-Djava.class.path=..."
    void *extraInfo;
} JavaVMOption;

2. 啟動器核心流程

  1. LoadLibrary("jvm.dll") 載入 JVM 動態庫
  2. GetProcAddress 獲取 JNI_CreateJavaVM 函數指針
  3. 構造 JavaVMInitArgs:類路徑、庫路徑、JIT 開關等
  4. 調用 JNI_CreateJavaVM(&jvm, &env, &vm_args) 創建 JVM 實例
  5. 通過 JNIEnv 尋找主類、獲取 main 方法 ID、調用 CallStaticVoidMethod 啟動 Java 應用

3. 關鍵代碼片段

JavaVMInitArgs vm_args;
JavaVMOption options[4];
options[0].optionString = "-Djava.compiler=NONE";
options[1].optionString = "-Djava.class.path=c:\\myclasses";
options[2].optionString = "-Djava.library.path=c:\\mylibs";
options[3].optionString = "-verbose:jni";

vm_args.version = JNI_VERSION_1_2;
vm_args.options = options;
vm_args.nOptions = 4;
vm_args.ignoreUnrecognized = JNI_TRUE;

typedef jint (JNICALL *CreateJavaVM_t)(JavaVM**, void**, void*);
HINSTANCE hinst = LoadLibrary("jvm.dll");
CreateJavaVM_t pfnCreateJavaVM = (CreateJavaVM_t)GetProcAddress(hinst, "JNI_CreateJavaVM");

JavaVM *jvm; JNIEnv *env;
jint res = pfnCreateJavaVM(&jvm, (void**)&env, &vm_args);
if (res < 0) { /* error handling */ }

// 啟動主類
jclass cls = (*env)->FindClass(env, mainClassName);
jmethodID mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
(*env)->CallStaticVoidMethod(env, cls, mid, appArgs);

編譯與部署

# Cygwin Shell 中編譯
g++ cvm.cpp -o javaw.exe
  1. 備份原 JRE bin/javaw.exe,替換為編譯產出的 javaw.exe
  2. 確保 Eclipse 使用該 JRE
  3. 從 Cygwin Shell 啟動 Eclipse:export PATH=/cygdrive/c/tools/eclipse3.2/:$PATH; eclipse
  4. 若 JNI 庫路徑不在系統 PATH,補充:export PATH=/cygdrive/c/tutorial/jni_libs/:$PATH

注意事項

  • 啟動器需根據目標 JRE/JDK 版本調整 JVM 參數結構,文中測試環境為 j2re1.4.2_06
  • 僅處理基礎 JVM 參數,複雜場景需擴展參數解析邏輯
  • 源碼下載:cvm_src.zip / cvm.zip

原理總結

標準啟動器 = 非 Cygwin 進程 → 加載 Cygwin JNI 庫死鎖
自製啟動器 = Cygwin 進程 → 擁有 Cygwin 上下文 → 正常加載 JNI 庫

此方案在 Eclipse + Cygwin + JNI 混合開發場景下經過實戰驗證,屬於「治標又治本」的工程級變通方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved