筆者在前段時間碰到這么一種情況,即在兩個項目中使用了相同的applet,然后在applet中調用了dll操作(使用jni或jna),然后在客戶端進行調用。實際的訪問過程如下:
首先訪問項目A的一個界面,界面中調用了appletA,接着並沒有關閉瀏覽器而直接訪問項目B的界面,在界面中調用了appletB。appletA和appletB實際上是同一個applet,只不過這個applet使用在了兩個項目中,並且兩個項目均是直接進行訪問。這時候在訪問appletB的時候,就會出現一個錯誤:
xxx NOT loaded java.lang.UnsatisfiedLinkError : Native Library XXX.dll already loaded in another classloader
如果訪問從appletB到appletA,那么在訪問appletA時也會出現同樣的錯誤。
因為,在一個標簽頁中,多個applet運行實際上是運行在同一個jvm上,只是加載applet時使用了不同的classLoader。因此,不管是 appletA先運行還是appletB先運行,最終情況都是所依賴的dll都會被同一個jvm所加載,就會出現以上的錯誤了。
在進行google之后,發現很多開發人員都碰到了同樣的問題,有的是因為在同一個javaEE容器如(weblogic,jboss)中部署了兩個都要 訪問同一個jni調用的項目,有的則是像筆者同樣的經歷。最后的結論即是,在一個jvm當中,是不允許加載一個dll兩次的。因此,后面的jni調用時, 嘗試再次加載同一個dll,這時候即會報上面的錯誤了。因為該錯誤,相對應的java類肯定不能被初始化,因此相應的項目或者applet肯定啟動不了 了。
解決這個問題其實很簡單,將訪問到jni的代碼單獨提取出來,並不直接讓項目自身的classLoader加載,則是讓其由systemLoader加載 即可。一種方法就是將這部分代碼,單獨封裝成一個jar,放到java的systemLoader可以加載的地方,如lib/ext目錄下。然后,項目中 仍然去調用此代碼。由於訪問dll的代碼由systemLoader加載,因此,多個項目同時訪問同一個dll時,即可避免再次加載了。因為,第二個項目 在訪問時,尋找到的類,已經被systemLoader加載過了,因此項目本身的classLoader就不會再去加載這個類了。
對比,原來的appletA,appletB,修改過后就成了這樣的結構:appletA,appletB,以及jniAccess.jar,其中jniAccess.jar放到jre的lib目錄的ext目錄下。這樣,再次訪問applet,就沒有問題了。
(原文)http://www.iflym.com/index.php/code/resolve-java-lang-unsatisfiedlinkerror-native-library-dll-already-loaded-in-another-classloader-problem.html