一個jvm中的類只能加載一次?

在網上看到有些人說“一個類在一個jvm中只能加載一次”,對此產生了懷疑。

另外,在用flink、spark做計算的時候,有一個疑惑,如果用相同的jar包啓動了相同的任務,而這兩個任務被分配到了同一個進程的不同線程裏,是不是意味着這兩個任務是共用類的?如果是這樣,寫flink程序的時候,操作類成員變量豈不是成爲一種很危險的動作?

 

然後我就嘗試去研究了一下flink的源碼,只看到了一小部分,結合一些實驗+一些大膽猜測得出結論如下:一個類在一個jvm裏可以加載多次,而flink的兩個任務不會共用相同的類(即便他們是來自相同jar包的同名類)。

 

得出這個結論只需要瞭解java的類加載機制。java中有四種類加載器,程序運行需要的類都會由他們加載到內存中。這四種種類加載器分別是BootStrap ClassLoader、Extension ClassLoader、Application ClassLoader、自定義ClassLoader。

一個類由它的全路徑限定符和加載它的classLoader決定,也就是說同一個jar中的class如果被不同的類加載器進行了加載,那麼這個類在內存就會存在兩份,而且兩個類並不相同。

看如下例子:(參考自https://www.iteye.com/blog/yongxin10020071209191159-252393

待加載類:

import java.util.Random;  
  
  public class IntProducer  
  {  
        //用隨機值來加載靜態域賦值,如果被加載兩次age應該會是不同的值  
    public static int age = getRandom();  
  
    public static int getRandom()  
    {  
        Random ran = new Random(System.nanoTime());  
        int rand = ran.nextInt(10);  
        System.out.println("IntProducer 被加載,產生隨機數:"+rand);  
        return rand;  
  
    }  
  
 }  

加載測試:

import java.net.URL;
import java.net.URLClassLoader;

public class Main {
    public static void main(String[] args) throws Exception
    {
        System.out.println(Main.class.getClassLoader());


        URL[] us = { new URL("file:///Users/zgy/test/") };

        ClassLoader loader1 = new URLClassLoader(us);
        Class c1 = loader1.loadClass("IntProducer");

        System.out.println(c1.getClassLoader()+":"+c1.getField("age").getInt(c1));

        //再加載一次
        ClassLoader loader2 = new URLClassLoader(us);
        Class c2 = loader2.loadClass("IntProducer");
        System.out.println(c2.getClassLoader()+":"+c2.getField("age").getInt(c2));

    }
}

輸出如下:

sun.misc.Launcher$AppClassLoader@18b4aac2
IntProducer 被加載,產生隨機數:1
java.net.URLClassLoader@61bbe9ba:1
IntProducer 被加載,產生隨機數:0
java.net.URLClassLoader@1d44bcfa:0

可以看到同一個類被加載了兩次,對應的classLoader是不一樣的。

根據flink的任務提交代碼,可以看到在任務分發的時候,同時將classLoader也發送了出去,可以推測出,在任務運行的線程中會恢復這個classLoader,並使用此classLoader加載需要的類。

 

然後,轉折來了。標題裏的說法就是錯的嗎?也不全是,只是需要加上限定條件。在不指定ClassLoader的時候,程序兩次加載同一個類,確實只是會加載一次,這個是完全必要的,因爲只有這樣才能保證類加載的安全。否則,想用到一個類,加載兩次反而是不一樣的,豈不是瘋了。jvm專門設計了雙親委派模型,來保證這種加載機制。

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