Flink的classLoader加載機制(推測)-- 記一次程序問題中的探索

    項目中需要用flink去加載c++的so文件。flink任務中如果有加載so的邏輯,當任務掛掉之後,再次重啓的時候會報“Native Library xxx is being loaded in another classloader”的錯誤(這個過程中Flink集羣的進程沒退出)。

    通過分析System.loadLibrary(String libname)方法,發現加載後的so文件會記錄在內存裏,會記錄一些信息,包括so文件名、加載這個so的類所用的classLoader(每個類都是被一個classLoader加載的,如果同一個類被不同的classLoader加載,這個類在內存裏是不一樣的,雖然他們的源頭相同)。如果在加載某個so文件的時候,發現該so文件被加載過了,且加載這個so的classLoader和當前classLoader不相同,那麼就會報上面的錯誤。

     由此推測,這個應該是flink每個任務都有自己的classLoader(這個在看flink任務分發的代碼時有看到過,jobmanager把解析後的job分發給taskmanager的時候會把jobmanager中使用的classloader也發出去),so是任務自定義的classLoader加載的,當任務重啓的時候,又生成了一個新的classLoader,它去加載so,結果發現已經加載過了。

    這個問題推測可以用一下兩種方法解決:

     (1)每次修改so文件的文件名,這樣每次加載的so文件不是一樣的,也就不衝突了。(已實驗過,可行)

     (2)用更高層的classLoader進行so的加載,比如bootStrapClassLoader,這種是進程共享的,這就避免了每次都由任務自己的ClassLoader去加載so文件。

 

第一個方案使用過,是可行的。第二個方案在理論上進行了實驗,並沒有真正的使用在項目中。

     

使用ExtClassLoader加載so文件

研究了一下java的ClassLoader機制,感覺可以用ExtClassLoader加載對應的類,讓這個類去load   so文件

那麼,怎麼能讓類被ExtClassLoader加載呢?做了一個小實驗,說明這個邏輯。

A.java

public class A {
    public static String s = "test";
    public static void main(String[] args) {
        System.out.println(A.class.getClassLoader());
    }
}
$ javac A.java
$ jar -cvf A.jar A.class
# 打好的jar包中有A.class 和META-INF/MANIFEST.MF。但是META-INF/MANIFEST.MF文件中沒有指定Main,所以不可以直接執行,要想直接執行,採用下面的操作
$ jar -xf A.jar
# 給MANIFEST.MF 文件加入“Main-Class: A”,然後重新打包
$ jar -cvfm A.jar META-INF/MANIFEST.MF A.class
$ java -jar A.jar
sun.misc.Launcher$AppClassLoader@7852e922


# 然後,把A.jar放到${JAVA_HOME}/jre/lib/ext目錄下
$ java -jar A.jar
sun.misc.Launcher$ExtClassLoader@6d06d69c

上面可以看出,把jar包放到ext目錄下之後,A類被擴展類加載器加載,其他所有的類都可以調用這個類使用。

那麼,可以把loadLibrary的邏輯放在ext目錄下,這樣不管是哪個程序調用這個類,去加載so的時候都不會重複加載,而且只要jvm還存在,這個加載關係就一直能存在。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章