1. 程式人生 > >Android的so檔案載入機制詳解

Android的so檔案載入機制詳解

640?wx_fmt=png


今日科技快訊


10月30日,小米集團跌超4%,再創上市以來新低,市值下破2600億港元關口。此前,財政部發布的《2018年會計資訊質量檢查公告》顯示,在2017年度會計執法檢查中發現,部分企業跨境轉移利潤、逃避繳納稅收等問題比較突出。在被點名的網際網路企業中,就包括小米公司。


作者簡介


本篇來自 請叫我大蘇 的投稿,詳細地為大家解析了so檔案的載入機制一起來看看!希望大家喜歡。

 請叫我大蘇 的部落格地址:

https://www.jianshu.com/u/bb52a2918096


前言


本文的結論是跟著 System.loadlibrary() 一層層原始碼走進去,個人對其的理解所整理的,那麼開始看原始碼之前,先來提幾個問題:

Q1:你知道 so 檔案的載入流程嗎?

Q2:裝置存放 so 的路徑有 system/lib,vendor/lib,system/lib64,vendor/lib64,知道在哪裡規定了這些路徑嗎?清楚哪些場景下系統會去哪個目錄下尋找 so 檔案嗎?還是說,所有的目錄都會去尋找?

Q3:Zygote 程序是分 32 位和 64 位的,那麼,系統是如何決定某個應用應該執行在 32 位上,還是 64 位上?

Q4:如果程式跑在 64 位的 Zygote 程序上時,可以使用 32 位的 so 檔案麼,即應用的 primaryCpuAbi 為 arm64-v8a,那麼是否可使用 armeabi-v7a 的 so 檔案,相容的嗎?

Q2,Q3,Q4,這幾個問題都是基於裝置支援 64 位的前提下,在舊系統版本中,只支援 32 位,也就沒這麼多疑問需要處理了。


準備工作


由於這次的原始碼會涉及很多 framework 層的程式碼,包括 java 和 c++,直接在 AndroidStudio 跟進 SDK 的原始碼已不足夠檢視到相關的程式碼了。所以,此次是藉助 Source Insight 軟體,而原始碼來源如下

https://android.googlesource.com/platform/

我並沒有將所有目錄下載下來,只下載瞭如下目錄的原始碼:

  • system/core

  • bionic

  • libcore

  • dalvik

  • frameworks/base

  • frameworks/native

我沒有下載最新版本的程式碼,而是選擇了 Tags 下的 More 按鈕,然後選擇 tag 為: android-5.1.1 r24 的程式碼下載。所以,此次分析的原始碼是基於這個版本,其餘不同版本的程式碼可能會有所不一樣,但大體流程應該都是一致的。


分析


原始碼分析的過程很長很長,不想看過程的話,你也可以直接跳到末尾看結論,但就會錯失很多細節的分析了。

那麼下面就開始來過下原始碼吧,分析的入口就是跟著 System.loadlibrary() 走 :

//System#loadlibrary()
public static void loadLibrary(String libName) {
    Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}

//Runtime#loadLibrary()
void loadLibrary(String libraryName, ClassLoader loader) {
    //1. 程式中通過 System.loadlibrary() 方式,這個 loader 就不會為空,流程走這邊
    if (loader != null) {
        //2. loader.findLibrary() 這是個重點,這個方法用於尋找 so 檔案是否存在
        String filename = loader.findLibrary(libraryName);
        if (filename == null) {
             throw new UnsatisfiedLinkError(loader + " couldn't find \"" + System.mapLibraryName(libraryName) + "\"");
        }
        //3. 如果 so 檔案找到,那麼載入它
        String error = doLoad(filename, loader);
        if (error != null) {
            //4. 如果載入失敗,那麼拋異常
            throw new UnsatisfiedLinkError(error);
        }
        return;
    }

    //1.1 以下程式碼的執行場景我不清楚,但有幾個方法可以蠻看一下
    //mapLibraryName 用於拼接 so 檔名的字首:lib,和字尾.so
    String filename = System.mapLibraryName(libraryName);
    //...省略
    //1.2 mLibPaths 儲存著裝置存放 so 檔案的目錄地址
    for (String directory: mLibPaths) {
        String candidate = directory + filename;
        candidates.add(candidate);
        if (IoUtils.canOpenReadOnly(candidate)) 
            // 1.3 呼叫 native 層方法載入 so 庫
            String error = doLoad(candidate, loader);
            if (error == null) {
                return// We successfully loaded the library. Job done.
            }
            lastError = error;
        }
    }
    //...省略
}

所以,其實 System 的 loadlibrary() 是呼叫的 Runtime 的 loadLibrary(),不同系統版本,這些程式碼是有些許差別的,但不管怎樣,重點都還是 loadLibrary() 中呼叫的一些方法,這些方法基本沒變,改變的只是其他程式碼的優化寫法。

那麼,要理清 so 檔案的載入流程,或者說,要找出系統是去哪些地址載入 so 檔案的,就需要梳理清這些方法:

  • loader.findLibrary()

  • doLoad()

第一個方法用於尋找 so 檔案,所涉及的整個流程應該都在這個方法裡,如果可以找到,會返回 so 檔案的絕對路徑,然後交由 doLoad() 去載入。

java.library.path

但在深入去探索之前,我想先探索另一條分支,loader 為空的場景。loader 什麼時候為空,什麼時候不為空,我並不清楚,只是看別人的文章分析時說,程式中通過 System.loadlibrary() 方式載入 so,那麼 loader 就不會為空。那,我就信你了,不然我也不知道去哪分析為不為空的場景。

既然程式不會走另一個分支,為什麼我還要先來探索它呢?因為,第一個分支太不好探索了,先從另一個分支摸索點經驗,而且還發現了一些感覺可以拿來講講的方法:

  • System.mapLibraryName()

用於拼接 so 檔名的字首 lib,和字尾 .so。

  • mLibPaths

在其他版本的原始碼中,可能就沒有這個變量了,直接就是呼叫一個方法,但作用都一樣,我們看看這個變數的賦值:

//Runtime.mLibPaths
private final String[] mLibPaths = initLibPaths();

//Runtime#initLibPaths()
private static String[] initLibPaths() {
    String javaLibraryPath = System.getProperty("java.library.path");
    //...省略
}

最後都是通過呼叫 System 的 getProperty() 方法,讀取 java.library.path 的屬性值。

也就是說,通過讀取 java.library.path 的系統屬性值,是可以獲取到裝置存放 so 庫的目錄地址的,那麼就來看看在哪裡有設定這個屬性值進去。

System 內部有一個型別為 Properties 的靜態變數,不同版本,這個變數名可能不一樣,但作用也都一樣,用來儲存這些系統屬性值,這樣程式需要的時候,呼叫 getProperty() 讀取屬性值時其實是來這個靜態變數中讀取。而變數的初始化地方在類中的 static 程式碼塊中:

//System
static {
    //...省略
    //1.初始化一些不變的系統屬性值
    unchangeableSystemProperties = initUnchangeableSystemProperties();
    //2.將上述的屬性值以及一些預設的系統屬性值設定到靜態變數中
    systemProperties = createSystemProperties();
    //...
}

//System#initUnchangeableSystemProperties()
private static Properties initUnchangeableSystemProperties() {
    //...省略一些屬性值設定
    p.put("java.vm.vendor", projectName);
    p.put("java.vm.version", runtime.vmVersion());
    p.put("file.separator""/");
    p.put("line.separator""\n");
    p.put("path.separator"":");
    //...

    //1.這裡是重點
    parsePropertyAssignments(p, specialProperties());

    //...
    return p;
}

//System#createSystemProperties()
private static Properties createSystemProperties() {
    //1.拷貝不可變的一些系統屬性值
    Properties p = new PropertiesWithNonOverrideableDefaults(unchangeableSystemProperties);
    //2.設定一些預設的屬性值
    setDefaultChangeableProperties(p);
    return p;
}

//System#setDefaultChangeableProperties()
private static void setDefaultChangeableProperties(Properties p) {
    p.put("java.io.tmpdir""/tmp");
    p.put("user.home""");
}

static 靜態程式碼塊中的程式碼其實就是在初始化系統屬性值,分兩個步驟,一個是先設定一些不可變的屬性值,二是設定一些預設的屬性值,然後將這些儲存在靜態變數中。

但其實,不管在哪個方法中,都沒找到有設定  java.library.path 屬性值的程式碼,那這個屬性值到底是在哪裡設定的呢?

關鍵點在於設定不可變的屬性時,有呼叫了一個 native 層的方法:

//System
/**
* Returns an array of "key=value" strings containing information not otherwise
* easily available, such as #defined library versions.
*/

private static native String[] specialProperties();

這方法會返回 key=value 形式的字串陣列,然後 parsePropertyAssignments() 方法會去遍歷這些陣列,將這些屬性值填充到儲存系統屬性值的靜態變數中。

也就是說,在 native 層還會設定一些屬性值,而 java.library.path 有可能就是在 native 中設定的,那麼就跟下去看看吧。

System 連同包名的全名是:java.lang.System;那麼,通常,所對應的 native 層的 cpp 檔名為:java_lang_System.cpp,到這裡去看看:

//platform/libcore/luni/src/main/native/java_lang_System.cpp#System_specialProperties()
static jobjectArray System_specialProperties(JNIEnv* env, jclass) {
    std::vector<std::string> properties;

    //...

    //1. 獲取 LD_LIBRARY_PATH 環境變數值
    const char* library_path = getenv("LD_LIBRARY_PATH");
#if defined(HAVE_ANDROID_OS)
    if (library_path == NULL) 
{
        //2.如果 1 步驟沒獲取到路徑,那麼通過該方法獲取 so 庫的目錄路徑
        android_get_LD_LIBRARY_PATH(path, sizeof(path));
        library_path = path;
    }
#endif
    if (library_path == NULL) 
{
        library_path = "";
    }
    //3.設定 java.library.path 屬性值
    properties.push_back(std::string("java.library.path=") + library_path);

    return toStringArray(env, properties);
}

沒錯吧,對應的 native 層的方法是上述這個,它乾的事,其實也是設定一些屬性值,我們想要的 java.library.path 就是在這裡設定的。那麼,這個屬性值來源的邏輯是這樣的:

  1. 先讀取 LD_LIBRARY_PATH 環境變數值,如果不為空,就以這個值為準。但我測試過,貌似,程式執行時讀取的這個值一直是 null,在 Runtime 的 doLoad() 方法註釋中,Google 有解釋是說由於 Android 的程序都是通過 Zygote 程序 fork 過來,所以不能使用 LD_LIBRARY_PATH 。應該,大概,可能是這個意思吧,我英文不大好,你們可以自行去確認一下。

  2. 也就是說,第一步讀取的 LD_LIBRARY_PATH  值是為空,所以會進入第二步,呼叫 android_get_LD_LIBRARY_PATH 方法來讀取屬性值。(進入這個步驟有個條件是定義了 HAVE_ANDROID_OS 巨集變數,我就不去找到底哪裡在什麼場景下會定義了,看命名我直接猜測 Android 系統就都有定義的了)

那麼,繼續看看 android_get_LD_LIBRARY_PATH  這個方法做了些什麼:

//platform/libcore/luni/src/main/native/java_lang_System.cpp
#if defined(HAVE_ANDROID_OS)
extern "C" void android_get_LD_LIBRARY_PATH(char*, size_t)
;
#endif

emmm,看不懂,頭疼。那,直接全域性搜尋下這個方法名試試看吧,結果在另一個 cpp 中找到它的實現:

//platform/bionic/linker/dlfcn.cpp
void android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
  ScopedPthreadMutexLocker locker(&g_dl_mutex);
  do_android_get_LD_LIBRARY_PATH(buffer, buffer_size);
}

第一行估計是加鎖之類的意思吧,不管,第二行是呼叫另一個方法,繼續跟下去看看:

//platform/bionic/linker/linker.cpp
void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
  //...
  char* end = stpcpy(buffer, kDefaultLdPaths[0]);
  *end = ':';
  strcpy(end + 1, kDefaultLdPaths[1]);
}

static const charconst kDefaultLdPaths[] = {
#if defined(__LP64__)
  "/vendor/lib64",
  "/system/lib64",
#else
  "/vendor/lib",
  "/system/lib",
#endif
  nullptr
}
;

還好 Source Insight 點選方法時有時可以支援直接跳轉過去,呼叫的這個方法又是在另一個 cpp 檔案中了。開頭省略了一些大小空間校驗的程式碼,然後直接複製了靜態常量的值,而這個靜態常量在這份檔案頂部定義。

終於跟到底了吧,也就是說,如果有定義了 __LP64__ 這個巨集變數,那麼就將 java.library.path 屬性值賦值為 "/vendor/lib64:/system/lib64",否則,就賦值為 "/vendor/lib:/system/lib"。

也就是說,so 檔案的目錄地址其實是在 native 層通過硬編碼方式寫死的,網上那些理所當然的說 so 檔案的存放目錄也就是這四個,是這麼來的。那麼,說白了,系統預設存放 so 檔案的目錄就兩個,只是有兩種場景。

而至於到底什麼場景下會有這個 __LP64__ 巨集變數的定義,什麼時候沒有,我實在沒能力繼續跟蹤下去了,網上搜索了一些資料後,仍舊不是很懂,如果有清楚的大佬,能夠告知、指點下就最棒了。

我自己看了些資料,以及,自己也做個測試:同一個 app,修改它的 primaryCpuAbi 值,呼叫 System 的 getProperty() 來讀取 java.library.path,它返回的值是會不同的。

所以,以我目前的能力以及所掌握的知識,我是這麼猜測的,純屬個人猜測:

__LP64__ 這個巨集變數並不是由安卓系統程式碼來定義的,而是 Linux 系統層面所定義的。在 Linux 系統中,可執行檔案,也可以說所執行的程式,如果是 32 位的,那麼是沒有定義這個巨集變數的,如果是 64 位的,那麼是有定義這個巨集變數的。

總之,通俗的聯想解釋,__LP64__ 這個巨集變量表示著當前程式是 32 位還是 64 位的意思。(個人理解)

有時間再繼續研究吧,反正這裡清楚了,系統預設存放 so 檔案的目錄只有兩個,但有兩種場景。vendor 較少用,就不每次都打出來了。也就是說,如果應用在 system/lib 目錄中沒有找到 so 檔案,那麼它是不會再自動去 system/lib64 中尋找的,兩者它只會選其一。至於選擇哪個,因為 Zygote 是有分 32 位還是 64 位程序的,那麼剛好可以根據這個為依據。

findLibrary

該走回主線了,在支線中的探索已經摸索了些經驗了。

大夥應該還記得吧,System 呼叫了 loadlibrary() 之後,內部其實是呼叫了 Runtime 的 loadLibrary() 方法,這個方法內部會去呼叫 ClassLoader 的 findLibrary() 方法,主要是去尋找這個 so 檔案是否存在,如果存在,會返回 so 檔案的絕對路徑,接著交由 Runtime 的 doLoad() 方法去載入 so 檔案。

所以,我們想要梳理清楚 so 檔案的載入流程,findLibrary() 是關鍵。那麼,接下去,就來跟著 findLibrary() 走下去看看吧:

//ClassLoader#findLibrary()
protected String findLibrary(String libName) {
    return null;
}

ClassLoader 只是一個基類,具體實現在其子類,那這裡具體執行的是哪個子類呢?

//System#loadlibrary()
public static void loadLibrary(String libName) {
    Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());
}

所以這裡是呼叫了 VMStack 的一個方法來獲取 ClassLoader 物件,那麼繼續跟進看看:

native public static ClassLoader getCallingClassLoader();

又是一個 native 的方法,我嘗試過跟進去,沒有看懂。那麼,換個方向來找出這個基類的具體實現子類是哪個吧,很簡單的一個方法,打 log 輸出這個物件本身:

ClassLoader classLoader = getClassLoader();
Log.v(TAG, "classLoader = " + classLoader.toString());
//輸出
// classLoader = dalvik.system.PathClassLoader[dexPath=/data/app/com.qrcode.qrcode-1.apk,libraryPath=/data/app-lib/com.qrcode.qrcode-1]

以上打 Log 程式碼是從 Java中System.loadLibrary() 的執行過程 這篇文章中截取出來的,使用這個方法的前提是你得清楚 VMStack 的 getCallingClassLoader() 含義其實是獲取呼叫這個方法的類它的類載入器物件。

或者,你對 Android 的類載入機制有所瞭解,知道當啟動某個 app 時,經過層層工作後,會接著讓 LoadedApk 去載入這個 app 的 apk,然後通過 ApplicationLoader 來載入相關程式碼檔案,而這個類內部是例項化了一個 PathClassLoader 物件去進行 dex 的載入。

不管哪種方式,總之清楚了這裡實際上是呼叫了 PathClassLoader 的 findLibrary() 方法,但 PathClassLoader  內部並沒有這個方法,它繼承自 BaseDexClassLoader,所以實際上還是呼叫了父類的方法,跟進去看看:

//platform/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
public String findLibrary(String name) {
    return pathList.findLibrary(name);
}

private final DexPathList pathList;

內部又呼叫了 DexPathList 的 findLibrary() 方法,繼續跟進看看:

//platform/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public String findLibrary(String libraryName) {
    //1. 拼接字首:lib,和字尾:.so
    String fileName = System.mapLibraryName(libraryName);
    //2. 遍歷所有存放 so 檔案的目錄,確認指定檔案是否存在以及是隻讀檔案
    for (File directory: nativeLibraryDirectories) {
        String path = new File(directory, fileName).getPath();
        if (IoUtils.canOpenReadOnly(path)) {
            return path;
        }
    }
    return null;
}

/** List of native library directories. */
private final File[] nativeLibraryDirectories;

到了這裡,會先進行檔名補全操作,拼接上字首:lib 和字尾:.so,然後遍歷所有存放 so 檔案的目錄,當找到指定檔案,且是隻讀屬性,則返回該 so 檔案的絕對路徑。

所以,重點就是 nativeLibraryDirectories 這個變量了,這裡存放著 so 檔案儲存的目錄路徑,那麼得看看它在哪裡被賦值了:

//platform/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) {
    //...
    //1. 唯一賦值的地方,建構函式
    this.nativeLibraryDirectories = splitLibraryPath(libraryPath);
}

private static File[] splitLibraryPath(String path) {
    // Native libraries may exist in both the system and
    // application library paths, and we use this search order:
    //
    //   1. this class loader's library path for application libraries
    //   2. the VM's library path from the system property for system libraries
    //   (翻譯下,大體是說,so 檔案的來源有兩處:1是應用自身存放 so 檔案的目錄,2是系統指定的目錄)
    // This order was reversed prior to Gingerbread; see http://b/2933456.
    ArrayList < File > result = splitPaths(path, System.getProperty("java.library.path"), true);
    return result.toArray(new File[result.size()]);
}

//將傳入的兩個引數的目錄地址解析完都存放到集合中
private static ArrayList < File > splitPaths(String path1, String path2, boolean wantDirectories) {
    ArrayList < File > result = new ArrayList < File > ();

    splitAndAdd(path1, wantDirectories, result);
    splitAndAdd(path2, wantDirectories, result);
    return result;
}

private static void splitAndAdd(String searchPath, boolean directoriesOnly, ArrayList < File > resultList) {
    if (searchPath == null) {
        return;
    }
    //因為獲取系統的 java.library.path 屬性值返回的路徑是通過 : 拼接的,所以先拆分,然後判斷這些目錄是否可用 
    for (String path: searchPath.split(":")) {
        try {
            StructStat sb = Libcore.os.stat(path);
            if (!directoriesOnly || S_ISDIR(sb.st_mode)) {
                resultList.add(new File(path));
            }
        } catch(ErrnoException ignored) {}
    }
}

所以,nativeLibraryDirectories 這個變數是在建構函式中被賦值。程式碼不多,總結一下,建構函式會傳入一個 libraryPath 引數,表示應用自身存放 so 檔案的路徑,然後內部會再去呼叫 System 的 getProperty("java.library.path") 方法獲取系統指定的 so 檔案目錄地址。最後,將這些路徑都新增到集合中。

而且,看新增的順序,是先新增應用自身的 so 檔案目錄,然後再新增系統指定的 so 檔案目錄,也就是說,當載入 so 檔案時,是先去應用自身的 so 檔案目錄地址尋找,沒有找到,才會去系統指定的目錄。

而系統指定的目錄地址在 native 層的 linker.cpp 檔案定義,分兩種場景,取決於應用當前的程序是 32 位還是 64 位,32 位的話,則按順序分別去 vendor/lib 和 system/lib 目錄中尋找,64 位則是相對應的 lib64 目錄中。

雖然,so 檔案載入流程大體清楚了,但還有兩個疑問點:

  • 構造方法引數傳入的表示應用自身存放 so 檔案目錄的 libraryPath 值是哪裡來的;

  • 應用什麼時候執行在 32 位或 64 位的程序上;

nativeLibraryDir

先看第一個疑問點,應用自身存放 so 檔案目錄的這個值,要追究的話,這是一個很漫長的故事。

這個過程,我不打算全部都貼程式碼了,因為很多步驟,我自己也沒有去看原始碼,也是看的別人的文章,我們以倒著追蹤的方式來進行追溯吧。

首先,這個 libraryPath  值是通過 DexPathList 的構造方法傳入的,而 BaseDexClassLoader 內部的 DexPathList 物件例項化的地方也是在它自己的構造方法中,同樣,它也接收一個 libraryPath 引數值,所以 BaseDexClassLoader 只是做轉發,來源並不在它這裡。

那麼,再往回走,就是 LoadedApk 例項化 PathClassLoader 物件的地方了,在它的 getClassLoader() 方法中:

//platform/frameworks/base/core/java/android/app/LoadedApk.java
public ClassLoader 

相關推薦

Android的so檔案載入機制

今日科技快訊 10月30日,小米集團跌超4%,再創上市以來新低,市值下破2600億港元關口。此前,財政部發布的《2018年會計資訊質量檢查公告》顯示,在2017年度會計執法檢查中發現,部分企業跨境轉移利潤、逃避繳納稅收等問題比較突出。在被點名的網際網路企業中,就包括

Java虛擬機器:類載入機制

    大家知道,我們的Java程式被編譯器編譯成class檔案,在class檔案中描述的各種資訊,最終都需要載入到虛擬機器記憶體才能執行和使用,那麼虛擬機器是如何載入這些class檔案的呢?在載入class檔案的過程中虛擬機器又幹了哪些事呢?今天我們來解密虛擬機器的類載入機制。

JVM 類載入機制

原文出處: ziwenxie 如下圖所示,JVM類載入機制分為五個部分:載入,驗證,準備,解析,初始化,下面我們就分別來看一下這五個過程。 載入 載入是類載入過程中的一個階段,這個階段會在記憶體中生成一個代表這個類的java.lang.Class物件,作為方法區這個類的

Java 類載入機制

一、類載入器   類載入器(ClassLoader),顧名思義,即載入類的東西。在我們使用一個類之前,JVM需要先將該類的位元組碼檔案(.class檔案)從磁碟、網路或其他來源載入到記憶體中,並對位元組碼進行解析生成對應的Class物件,這就是類載入器的功能。我們可以利用類載入器,實現類的動態載入。 二、類的

JVM類載入機制(一)JVM類載入過程

首先Throws(丟擲)幾個自己學習過程中一直疑惑的問題: 1、什麼是類載入?什麼時候進行類載入? 2、什麼是類初始化?什麼時候進行類初始化? 3、什麼時候會為變數分配記憶體? 4、什麼時候會為變數賦

java 的 ClassLoader 類載入機制

一個程式要執行,需要經過一個編譯執行的過程: Java的編譯程式就是將Java源程式 .java 檔案 編譯為JVM可執行程式碼的位元組碼檔案 .calss 。Java編譯器不將對變數和方法的引用編譯為數值引用,也不確定程式執行過程中的記憶體佈局,而是將這些符

【轉】kafka-檔案儲存機制

文章轉自“美團技術部落格”:https://tech.meituan.com/ Kafka是什麼 Kafka是最初由Linkedin公司開發,是一個分散式、分割槽的、多副本的、多訂閱者,基於zookeeper協調的分散式日誌系統(也可以當做MQ系統),常見可以用於web

JVM類載入機制(二)類載入器與雙親委派模型

1、通過一個類的全限定名(包名與類名)來獲取定義此類的二進位制位元組流(Class檔案)。而獲取的方式,可以通過jar包、war包、網路中獲取、JSP檔案生成等方式。 2、將這個位元組流所代表

TP5 自動載入機制

http://www.php.cn/php-weizijiaocheng-383032.html TP作為國內主流框架,目前已經發布了5.*版本,相對於3.*版本是進行了重構。今天我們就從原始碼來研究下TP5自動載入的實現。   作為單入口框架,就從入口檔案看起,按照tp5

Python檔案關閉機制

如果不用“with”,那麼Python會在何時關閉檔案呢?答案是:視情況而定。 Python程式設計師最初學到的東西里有一點就是可以通過迭代法很容易地遍歷一個開啟檔案的全文: Python f = open('/etc/

載入機制

之前在介紹JVM記憶體模型的時候(參看:JVM記憶體模型),提到了在執行時資料區之前,有個Class Loader,這個就是類載入器。用以把Class檔案中的描述資訊載入到記憶體中執行和使用。以下是《深入理解Java虛擬機器第二版》對類載入器機制的定義原文: 虛擬機器把描述類的資料從Class檔案載入到記憶

JVM類載入機制,建議看這一篇就夠了,深入淺出總結的十分詳細!

## 類載入機制 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。 ## 類載入的時機 * 遇到new(比如new Student())、getstatic和putstatic(讀取或設定

載入流程,類載入機制及自定義類載入(面試再也不怕了)

一、引言二、類的載入、連結、初始化1、載入1.1、載入的class來源2、類的連結2.1、驗證2.2、準備2.3、解析3、類的初始化3.1、< clinit>方法相關3.2、類初始化時機3.3、final定義的初始化3.4、ClassLoader只會對類進行載入,不會進行初始化三、類載入器1、JV

Java反射機制

java 反射 反射機制 工廠模式 1反射機制是什麽反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。在面向對象的世界裏,萬事萬物皆對象.在ja

Java垃圾回收(GC)機制

nbsp 引用計數 維護 png 對象 最新 新的 com 前沿 垃圾回收算法有兩種,根據不同的虛擬機策略不同 1、引用計數法 2、可達性分析法 由於我們平常使用的hotspot虛擬機用的是第二種。 那哪些是可達的呢? 這個算法的基本思想是通過一系列稱為“GC Roots”

Java的內存回收機制

out 結果 int destroy pan 得出 ida public toc http://blog.csdn.net/mengern/article/details/38150431 Java中提供了垃圾強制回收機制的方法System.gc(),但是系統並不保證會立即

Java反射機制

ride length 數組大小 conf array arraycopy 動態調用 ray info Java反射機制詳解 |目錄 1反射機制是什麽 2反射機制能做什麽 3反射機制的相關API ·通過一個對象獲得完整的包名和類名 ·實例化Class類對象 ·獲

Cookie/Session機制

order 隱藏對象 tro 這就是 緩存 cat 時域 共享 創建 會話(Session)跟蹤是Web程序中常用的技術,用來跟蹤用戶的整個會話。常用的會話跟蹤技術是Cookie與Session。Cookie通過在客戶端記錄信息確定用戶身份,Session通過在服務器端記錄

(轉)java的動態代理機制

spring throw system urn log enc before 代理類 三個參數 原文出自:http://www.cnblogs.com/xiaoluo501395377/p/3383130.html 在學習Spring的時候,我們知道Spring主要

JVM類加載機制(一)JVM類加載過程

進行 虛擬機啟動 類加載的時機 bsp 參與 tro ext 環境 java代碼 首先Throws(拋出)幾個自己學習過程中一直疑惑的問題: 1、什麽是類加載?什麽時候進行類加載? 2、什麽是類初始化?什麽時候進行類初始化? 3、什麽時候會為變量分配內存? 4、什麽時候會為