在Android上惠奸,訪問常量真的不會觸發(fā)類加載嗎?

一恰梢、背景

之前在搞Android產(chǎn)物字節(jié)碼分析時發(fā)現(xiàn)的一個問題佛南,這和一個老生常談的定論:“Java訪問常量不會觸發(fā)類加載”有密切關系,估計大家會感興趣嵌言,因此寫下來給大家分享下嗅回。

二、結(jié)論先行

在Android上摧茴,訪問一個常量(符號引用)也會觸發(fā)類加載

這和Android具體的代碼場景有關系绵载,感興趣可以接著往下看~

三、問題分析

3.1 工具利器

推薦工具 010 Editor苛白,它內(nèi)置了很多種二進制文件的結(jié)構(gòu)模版娃豹,可以幫助我們快速查看一些二進制文件的內(nèi)容,例如二進制xml购裙、dex懂版、arsc文件等。

~/.config/SweetScape/010 Editor.ini躏率,如果010editor過了30天試用期躯畴,可以刪除這個文件來清除記錄,即可接著使用薇芝。

推薦工具 python struct蓬抄,直接import struct就行『坏剑可以幫助我們按照字節(jié)快速解析數(shù)據(jù)嚷缭,例如:

struct.unpack('<HHI', f.read(8))

含義:讀取后8個字節(jié)的數(shù)據(jù),<代表按照小端序讀取黄娘,unpack的結(jié)果是一個數(shù)組峭状,HHI代表前兩個元素為short克滴,第三個元素為int,分別對應2优床、2劝赔、4個字節(jié)的數(shù)據(jù)。

3.2 知識儲備

3.2.1 dex的部分格式

dex最上層結(jié)構(gòu)還是比較清晰的:

[圖片上傳失敗...(image-529efb-1683790977467)]

我們需要關注其中的:

  • dex_string_ids dex字符串常量池

  • dex_type_ids dex中所有的類名描述

  • dex_field_ids dex中所有的字段描述胆敞,舉個例子

[圖片上傳失敗...(image-f9cc98-1683790977467)]

class_idx指的是它所在的類名索引(從dex_type_ids中查找)着帽,type_idx指的是類型名稱(從dex_type_ids中查找),name_idx指的是字段名索引(從dex_string_ids中查找)

  • dex_class_defs dex中所有類的描述移层,舉個例子:

[圖片上傳失敗...(image-6717c5-1683790977467)]

class_data指的是類里面的所有信息仍翰,其中中包含了數(shù)據(jù)static_fields,指的是類中所有的static字段的數(shù)據(jù):

[圖片上傳失敗...(image-bc1ec0-1683790977467)]

其中 field_idx_diff指的是這個字段在字段表里面的索引观话,access_flags指的是字段的修飾符予借,包括private、public频蛔、static等等

底部的static_values指的是static final常量的值灵迫,它和上面的class_data.static_fields中前面幾位static final常量是一一對應(按照順序?qū)┑模?/p>

[圖片上傳失敗...(image-5b6239-1683790977467)]

因為static變量是在類的<clinit>方法中進行賦值的,所以只有常量在static_values里面有值

由上可知:在dex中晦溪,static和static final字段儲存位置是一樣的瀑粥,僅修飾符不一樣

3.2.2 訪問static字段的指令

這個拿實際的例子比較好說明:

// 訪問一個static 變量/常量
R.string.dialog_loading_title

它對應的字節(jié)碼指令:

[圖片上傳失敗...(image-fe40c7-1683790977467)]

0160 03f3

忽略01,關鍵是 60 03f3:

60 代表指令 sget三圆,含義是獲取一個static字段

03f3 代表字段的索引狞换,十進制為1011,在對應的字段表中為:

[圖片上傳失敗...(image-165a40-1683790977467)]

通過03f3能從字段表中讀取該字段舟肉,能獲取到class_idx類索引修噪,可以拿到類的名稱。也就是通過 60 03f字節(jié)碼只能查詢到R.string.dialog_loading_title符號引用度气,但無法查詢到對應的值割按。

結(jié)合3.2.1的信息,其實已經(jīng)能猜出來磷籍,訪問常量同樣會觸發(fā)類加載适荣。因為訪問static字段的指令都是sget,訪問static變量肯定會觸發(fā)類加載院领,那訪問常量也是同樣的道理弛矛。

3.2.3 指令解析的部分源碼

這里直接貼出sget指令解析的源碼地址

經(jīng)過一系列的源碼追蹤,能找到最終解析 sget 后面的字段的地方:

[圖片上傳失敗...(image-3dcca1-1683790977467)]

該函數(shù)中調(diào)用了ResolveType 比然,參數(shù)傳入了class_idx丈氓,這和3.2.1節(jié)的內(nèi)容呼應。接著追蹤:

[圖片上傳失敗...(image-30c3ed-1683790977467)]

其實到這一步已有有答案了,F(xiàn)indClass接著就會去加載一個class 万俗。

不過相信大家還是對哪里使用的static_values感興趣湾笛,接著看:

在一個class初始化的時候:

[圖片上傳失敗...(image-b0249d-1683790977467)]

這里也是和3.2.1節(jié)內(nèi)容呼應,初始化一個class時會去獲取它在dex中的定義 ClassDef

截圖是在初始化class的static字段闰歪,而對static final的賦值請見EncodedStaticFieldValueIterator

[圖片上傳失敗...(image-15eb1b-1683790977467)]

[圖片上傳失敗...(image-10ad5e-1683790977466)]

這里的static_value_off_和3.2.1中的static_values對應嚎研,代表著常量的值

3.2.4 javac的內(nèi)聯(lián)優(yōu)化

在訪問一個常量時,javac總是會幫我們把常量的符號引用變成對值的直接引用库倘,所以從這個角度說临扮,訪問一個常量確實不會觸發(fā)類加載。例子:

class A {
  public static final NAME = "abc";
}

// 編譯前
String localName = A.NAME;

// 編譯后
String localName = “abc”

但凡事都有意外教翩,什么情況下常量不會被優(yōu)化呢杆勇?拿個實際的場景舉例子:

在Android的一個普通模塊中定義了新的資源,例如R.string.name這種的饱亿,這個模塊在編譯之后會產(chǎn)生R.jar蚜退,例如截圖:(class反編譯之后的)

[圖片上傳失敗...(image-91d617-1683790977466)]

這里的屬性全是static變量,而javac對不會對static變量做內(nèi)聯(lián)優(yōu)化彪笼。也就是說仍然存在著R.xxx.xxx的各種符號引用关霸,比如這種的:(class的圖形化展示 R.attr.submodule_a)

[圖片上傳失敗...(image-bc3e89-1683790977466)]

這個模塊的class文件會參與到App的編譯,但無需再走javac杰扫。App編譯時會對R.xx.xx賦值(注意上面截圖中屬性值都還是0)并將修飾符改為常量,例如產(chǎn)物:(App的最終產(chǎn)物dex中的R$string)

[圖片上傳失敗...(image-900c49-1683790977466)]

所以膘掰,在最終的代碼中就存在“對一個常量的符號引用”的情況

上述僅發(fā)生在debug包中章姓,打release包時還會對常量的符號引用進行內(nèi)聯(lián)

3.3 本地驗證

上面從理論上說通了訪問一個常量是可能造成類加載的。現(xiàn)在來進行本地驗證:

我這里就拿3.2.4中的普通模塊的R場景進行測試了识埋,測試關鍵代碼如下:

[圖片上傳失敗...(image-cf32e1-1683790977466)]

[圖片上傳失敗...(image-a3ab02-1683790977466)]

日志結(jié)果:

[圖片上傳失敗...(image-d4efcf-1683790977466)]

符合預期凡伊,訪問常量(符號引用)確實造成了類加載!

注意:實驗只能在自定義ClassLoader(插件化)的情況下測試窒舟,因為對于宿主源碼的ClassLoader是PathClassLoader系忙。PathClassLoader在調(diào)用findLoadedClass時就會“偷摸”著把class加載了,會導致rIsLoaded方法第一次訪問時就直接返回true了惠豺。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末银还,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子洁墙,更是在濱河造成了極大的恐慌蛹疯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件热监,死亡現(xiàn)場離奇詭異捺弦,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門列吼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幽崩,“玉大人,你說我怎么就攤上這事寞钥』派辏” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵凑耻,是天一觀的道長太示。 經(jīng)常有香客問我,道長香浩,這世上最難降的妖魔是什么类缤? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮邻吭,結(jié)果婚禮上餐弱,老公的妹妹穿的比我還像新娘。我一直安慰自己囱晴,他們只是感情好膏蚓,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著畸写,像睡著了一般驮瞧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上枯芬,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天论笔,我揣著相機與錄音,去河邊找鬼千所。 笑死狂魔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的淫痰。 我是一名探鬼主播最楷,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼待错!你這毒婦竟也來了籽孙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤朗鸠,失蹤者是張志新(化名)和其女友劉穎蚯撩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烛占,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡胎挎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年沟启,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片犹菇。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡德迹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揭芍,到底是詐尸還是另有隱情胳搞,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布称杨,位于F島的核電站肌毅,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏姑原。R本人自食惡果不足惜悬而,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望锭汛。 院中可真熱鬧笨奠,春花似錦、人聲如沸唤殴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朵逝。三九已至蔚袍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間配名,已是汗流浹背页响。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留段誊,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓栈拖,卻偏偏與公主長得像连舍,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子涩哟,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1.手畫一下Android系統(tǒng)架構(gòu)圖索赏,描述一下各個層次的作用? Android系統(tǒng)架構(gòu)圖 從上到下依次分為六層: ...
    __素顏__閱讀 5,914評論 1 107
  • layout: wikititle: Android逆向分析筆記categories: Reverse_Engin...
    超哥__閱讀 10,697評論 1 17
  • 代碼編譯的結(jié)構(gòu):本地機器碼--->字節(jié)碼贴彼。越來越多的程序語言選擇了與操作系統(tǒng)和機器指令集無關的潜腻、平臺中立的格式作為...
    一條小袍袍YoY閱讀 245評論 0 0
  • 類加載機制 如下圖所示,JVM類加載機制分為五個部分:加載器仗,驗證融涣,準備童番,解析,初始化威鹿,下面我們就分別來看一下這五個...
    舉頭望明月泣閱讀 1,144評論 0 0
  • JAVA 類的加載過程 Child c= new Child ();為例進行說明1).因為new用到了Child....
    Rtia閱讀 748評論 0 1