在開發 Android App 的過程中,專案會因為功能需求引入一些 opensource 套件包、lib、jar等資源,當專案日益龐大,就有可能發生 LinearAlloc exceeded capacity 的問題。本篇就來簡單介紹一下原因以及可行的解決方法
首先,先簡單介紹 LinearAlloc
LinearAlloc (線性分配器)
- source code:android_src/dalvik/vm/ 下 LinearAlloc.h 和 LinearAlloc.c
- 目的:主要用来管理 Dalvik 中 class 加載時的内存,使其簡單、快速地分配内存。簡單來說就是讓app在執行時,減少系統內存的佔用。
- 屬於 Android Running 層,由 Dalvik Virtual Machine 執行
Dalvik 與 DEX 文件檔
- Android 上所提供的 ByteCode 虛擬器
- 目標:
(1) 要能運作在效能需求不高、較少的記憶體,與使用慢速的內部 Flash 儲存
(2) 作業系統要能支援虛擬記憶體,執行行程與執行緒(Process/Thread)
(3) 要具備使用者帳號安全管理機制(UID-based security mechanisms),並能透過 GNU C 編譯器編譯後運作在包括 Linux,BSD 與 Mac OS X 等 Unix 環境下
(4) 支援 Little-endian 與 Big-endian 處理器執行環境
(5) Class Data 尤其是共用的 ByteCode,要能跨行程共用,節省系統整體記憶體需求(參考Process Memory Map 目前包括相關的 jar 與所包含的 Bytecode Dex 檔案,都能跨行程在不同的 Dalvik 行程中共用
(6) 減少 Dalvik 應用程式載入啟動的成本,加速應用程式的反應時間
(7) Class Data 儲存在個別的檔案中,導致額外的儲存成本,針對儲存空間的需求需要多加注意
(8) 要從 Class Data 中讀取出資料數值(例如:整數或是字串)會增加不必要的成本,評估如何採用 C 的形式存取,會是有必要的
(9) ByteCode 的驗證雖耗時但卻是必要的,應該試著在程式執行前完成驗證
(10) 透過快速指令集與機制的優化進行 ByteCode 的最佳化對於執行效率與電池壽命是相當重要的
(11) 為了安全需求,執行中的行程不能修改共享的程式碼 - 核心的函式庫主要是承襲 Open Source 的 Java SE 實作 Apache Harmony 而來,並基於 Open Source 中相關 OpenSSL, zlib 與 ICU(International Components for Unicode) 的計畫成果
※ 一般虛擬器的設計,會在應用程式啟動時,動態的從儲存裝置讀取並解壓縮個別的 Classes 到記憶體中,在沒有經過適度優化的架構下,每個獨立的虛擬器行程,都會包含自己一份的相關 Classes 記憶體空間 - 基於上述目標, Dalvik 在設計時,作了以下的決定:
(1) 可以把多個 Classes 檔案整合到一個 DEX 檔案中
(2) DEX 檔案會以唯讀方案載入到記憶體中,並跨行程共享
(3) 因應支援的系統架構,調整 Byte Ordering 與 Word Alignment
(4) ByteCode 驗證對所有的 Classes 都是必要的,且會儘可能進行事先的驗證加速系統效率
(5) 對於需修改 ByteCode 的最佳化動作,會在執行前完成
APK文件檔
- 屬於一種壓縮檔案,可用 zip 解壓縮
- 包含:
(1) META-INF:清單信息(Manifest file),應用程序的證書和授權信息,SHA-1 信息資源列表
(2) res:APK所需要的資源文件夾
(3) AndroidManifest.xml:一個傳統的 Android 清單文件,用於描述該應用程序的名字、版本號、所需許可權、註冊的服務、連結的其他應用程序。該文件使用 XML 文件格式,可以編譯為二進制的XML
(4) classes.dex:classes 文件通過 DEX 編譯後的文件格式,用於在 Dalvik 虛擬機上運行的主要代碼部分
(5) resources.arsc:編譯後的二進制資源文件
LinearAlloc exceeded capacity Problem
- 原因:app 在安裝時會將 apk 中 dex 文件安裝到 dalvik-cache 目錄下,在安裝過程中發生的錯誤。在 Android 2.2 與 2.3 版本中,LinearAlloc buffer 只有 5MB,但較新的版本 buffer 則有 8 或 16MB ,也就是說如果您的app要支援 Android 2.3 或以下版本, dex 文件檔必須小於 5MB,如果超出則會出現類似以下的錯誤訊息
12-17 14:59:56.959: D/PackageManager(183): Scanning package tw.com.maxkit.android.kokola.mobile.activity
12-17 14:59:56.959: E/PackageManager(183): Package tw.com.maxkit.android.kokola.mobile.activity has mismatched uid: 10091 on disk, 10098 in settings
12-17 14:59:56.959: I/PackageManager(183): Linking native library dir for /data/app/tw.com.maxkit.android.kokola.mobile.activity-1.apk
12-17 14:59:59.639: W/dalvikvm(1891): Superclass of 'Lorg/apache/http/params/SyncBasicHttpParams;' is final 'Lorg/apache/http/params/BasicHttpParams;'
12-17 14:59:59.989: E/AlarmManagerService(183): android_server_AlarmManagerService_set to type=1, 1387263691.768000000
12-17 14:59:59.989: E/AlarmManagerService(183): android_server_AlarmManagerService_set to type=1, 1387263660.000000000
12-17 14:59:59.989: D/PowerManagerService(183): acquireWakeLock flags=0x1 tag=AlarmManager
12-17 15:00:00.009: D/DigitalClock(32302): syw.action = android.intent.action.TIME_TICK
12-17 15:00:00.029: D/PowerManagerService(183): releaseWakeLock flags=0x1 tag=AlarmManager
12-17 15:00:02.939: E/dalvikvm(1891): LinearAlloc exceeded capacity (5242880), last=100
12-17 15:00:02.939: E/dalvikvm(1891): VM aborting
12-17 15:00:03.049: I/DEBUG(521): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
12-17 15:00:03.049: I/DEBUG(521): Build fingerprint: 'samsung/GT-S7500/GT-S7500:2.3.6/GINGERBREAD/ZSLD3:user/release-keys'
12-17 15:00:03.049: I/DEBUG(521): pid: 1891, tid: 1891 >>> /system/bin/dexopt <<<
12-17 15:00:03.049: I/DEBUG(521): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr deadd00d
12-17 15:00:03.049: I/DEBUG(521): r0 fffffe7c r1 deadd00d r2 00000026 r3 00000000
12-17 15:00:03.049: I/DEBUG(521): r4 8009d5b0 r5 004fffd4 r6 0050003c r7 0001a82c
12-17 15:00:03.049: I/DEBUG(521): r8 00000064 r9 00000001 10 00000000 fp befb99b8
12-17 15:00:03.049: I/DEBUG(521): ip 8009d6bc sp befb96e8 lr afd192e9 pc 80043ae4 cpsr 20000030
12-17 15:00:03.049: I/DEBUG(521): d0 74726f6261204d69 d1 646564656563786e
12-17 15:00:03.049: I/DEBUG(521): d2 0000000000000067 d3 000000000000000a
12-17 15:00:03.049: I/DEBUG(521): d4 0000000200000002 d5 0000000200000002
12-17 15:00:03.059: I/DEBUG(521): d6 00000002402c5020 d7 0000000200000002
12-17 15:00:03.059: I/DEBUG(521): d8 0000000000000000 d9 0000000000000000
12-17 15:00:03.059: I/DEBUG(521): d10 0000000000000000 d11 0000000000000000
12-17 15:00:03.059: I/DEBUG(521): d12 0000000000000000 d13 0000000000000000
12-17 15:00:03.059: I/DEBUG(521): d14 0000000000000000 d15 0000000000000000
12-17 15:00:03.059: I/DEBUG(521): d16 0000000200000002 d17 0000000200000002
12-17 15:00:03.059: I/DEBUG(521): d18 0000000200000002 d19 0000000200000002
12-17 15:00:03.059: I/DEBUG(521): d20 0000000c00000002 d21 0000000200000002
12-17 15:00:03.059: I/DEBUG(521): d22 402926d000000002 d23 4014ccf04014e928
12-17 15:00:03.059: I/DEBUG(521): d24 0000000000000000 d25 0000000000000000
12-17 15:00:03.069: I/DEBUG(521): d26 0000000000000000 d27 0000000000000000
12-17 15:00:03.069: I/DEBUG(521): d28 0000000000000000 d29 0000000000000000
12-17 15:00:03.069: I/DEBUG(521): d30 0000000000000000 d31 0000000000000000
12-17 15:00:03.069: I/DEBUG(521): scr 00000000
12-17 15:00:03.109: I/DEBUG(521): #00 pc 00043ae4 /system/lib/libdvm.so (dvmAbort)
12-17 15:00:03.109: I/DEBUG(521): #01 pc 0004b85e /system/lib/libdvm.so (dvmLinearAlloc)
12-17 15:00:03.109: I/DEBUG(521): #02 pc 0006535e /system/lib/libdvm.so
12-17 15:00:03.109: I/DEBUG(521): #03 pc 00065ca0 /system/lib/libdvm.so (dvmLinkClass)
12-17 15:00:03.109: I/DEBUG(521): #04 pc 00067210 /system/lib/libdvm.so
12-17 15:00:03.109: I/DEBUG(521): #05 pc 000673e6 /system/lib/libdvm.so (dvmFindSystemClassNoInit)
12-17 15:00:03.109: I/DEBUG(521): #06 pc 00066c5e /system/lib/libdvm.so (dvmFindClassNoInit)
12-17 15:00:03.109: I/DEBUG(521): #07 pc 00059908 /system/lib/libdvm.so (dvmOptResolveClass)
12-17 15:00:03.109: I/DEBUG(521): #08 pc 00055f66 /system/lib/libdvm.so
12-17 15:00:03.109: I/DEBUG(521): #09 pc 00057e64 /system/lib/libdvm.so
12-17 15:00:03.109: I/DEBUG(521): #10 pc 0005803a /system/lib/libdvm.so (dvmVerifyCodeFlow)
12-17 15:00:03.109: I/DEBUG(521): #11 pc 000597c8 /system/lib/libdvm.so
12-17 15:00:03.109: I/DEBUG(521): #12 pc 0005982a /system/lib/libdvm.so (dvmVerifyClass)
12-17 15:00:03.109: I/DEBUG(521): #13 pc 0005875a /system/lib/libdvm.so
12-17 15:00:03.119: I/DEBUG(521): #14 pc 0005880e /system/lib/libdvm.so
12-17 15:00:03.119: I/DEBUG(521): #15 pc 0005890a /system/lib/libdvm.so
12-17 15:00:03.119: I/DEBUG(521): #16 pc 00058af4 /system/lib/libdvm.so (dvmContinueOptimization)
12-17 15:00:03.119: I/DEBUG(521): #17 pc 00008c46 /system/bin/dexopt
12-17 15:00:03.119: I/DEBUG(521): #18 pc 00008d32 /system/bin/dexopt
12-17 15:00:03.119: I/DEBUG(521): #19 pc 00008dc8 /system/bin/dexopt
12-17 15:00:03.119: I/DEBUG(521): #20 pc 000091ce /system/bin/dexopt
12-17 15:00:03.119: I/DEBUG(521): #21 pc 00014c62 /system/lib/libc.so (__libc_init)
12-17 15:00:03.119: I/DEBUG(521): libc base address: afd00000
12-17 15:00:03.119: I/DEBUG(521): code around pc:
12-17 15:00:03.119: I/DEBUG(521): 80043ac4 447a4479 f7d34c0b 2000edca eecef7d3
12-17 15:00:03.119: I/DEBUG(521): 80043ad4 447c4809 6bdb5823 4798b103 22264902
12-17 15:00:03.119: I/DEBUG(521): 80043ae4 f7d3700a bf00ef36 deadd00d 00041204
12-17 15:00:03.119: I/DEBUG(521): 80043af4 00042a09 00059ad6 fffffe7c 4b09b40e
12-17 15:00:03.119: I/DEBUG(521): 80043b04 4c09b517 aa05447b f852591b 6b5b1b04
12-17 15:00:03.119: I/DEBUG(521): code around lr:
12-17 15:00:03.119: I/DEBUG(521): afd192c8 4a0e4b0d e92d447b 589c41f0 26004680
12-17 15:00:03.119: I/DEBUG(521): afd192d8 686768a5 f9b5e006 b113300c 47c04628
12-17 15:00:03.119: I/DEBUG(521): afd192e8 35544306 37fff117 6824d5f5 d1ef2c00
12-17 15:00:03.119: I/DEBUG(521): afd192f8 e8bd4630 bf0081f0 00028258 ffffff84
12-17 15:00:03.119: I/DEBUG(521): afd19308 b086b570 f602fb01 9004460c a804a901
12-17 15:00:03.119: I/DEBUG(521): stack:
12-17 15:00:03.119: I/DEBUG(521): befb96a8 00000002
12-17 15:00:03.119: I/DEBUG(521): befb96ac 00015270
12-17 15:00:03.119: I/DEBUG(521): befb96b0 0001a828
12-17 15:00:03.119: I/DEBUG(521): befb96b4 004ffdc8
12-17 15:00:03.119: I/DEBUG(521): befb96b8 afd4272c
12-17 15:00:03.119: I/DEBUG(521): befb96bc afd426d8
12-17 15:00:03.119: I/DEBUG(521): befb96c0 00000000
12-17 15:00:03.119: I/DEBUG(521): befb96c4 afd192e9 /system/lib/libc.so
12-17 15:00:03.119: I/DEBUG(521): befb96c8 00059ad6
12-17 15:00:03.119: I/DEBUG(521): befb96cc 004fffd4
12-17 15:00:03.119: I/DEBUG(521): befb96d0 0050003c
12-17 15:00:03.119: I/DEBUG(521): befb96d4 0001a82c
12-17 15:00:03.119: I/DEBUG(521): befb96d8 00000064
12-17 15:00:03.119: I/DEBUG(521): befb96dc afd183e1 /system/lib/libc.so
12-17 15:00:03.119: I/DEBUG(521): befb96e0 df002777
12-17 15:00:03.119: I/DEBUG(521): befb96e4 e3a070ad
12-17 15:00:03.119: I/DEBUG(521): #00 befb96e8 0001a828
12-17 15:00:03.119: I/DEBUG(521): befb96ec 8004b863 /system/lib/libdvm.so
12-17 15:00:03.119: I/DEBUG(521): #01 befb96f0 00000064
12-17 15:00:03.119: I/DEBUG(521): befb96f4 00000064
12-17 15:00:03.119: I/DEBUG(521): befb96f8 402f9080
12-17 15:00:03.119: I/DEBUG(521): befb96fc 8009d5b0
12-17 15:00:03.119: I/DEBUG(521): befb9700 00000019
12-17 15:00:03.119: I/DEBUG(521): befb9704 00000001
12-17 15:00:03.119: I/DEBUG(521): befb9708 fffffff7
12-17 15:00:03.119: I/DEBUG(521): befb970c 00000001
12-17 15:00:03.119: I/DEBUG(521): befb9710 00000000
12-17 15:00:03.119: I/DEBUG(521): befb9714 80065363 /system/lib/libdvm.so
12-17 15:00:03.139: I/DEBUG(521): dumpstate /data/log/dumpstate_app_native.txt
※ FaceBook也遇到相同問題:Dalvik patch for Facebook for Android,FaceBook Solve:Rebuilding Facebook for Android
2. Solve:
(1) 由上連結可知 FB 的 solve 是修改 LinearAlloc buffer,並盡可能減少內存記憶體的佔用
(2) 將 dex 文件切割成多個 dex 文件,並將其先下載至手機儲存後,在使用 DexClassLoader 動態載入 class (作法請參考下篇 使用 DexClassLoader 動態載入 DEX)
2. Solve:
(1) 由上連結可知 FB 的 solve 是修改 LinearAlloc buffer,並盡可能減少內存記憶體的佔用
(2) 將 dex 文件切割成多個 dex 文件,並將其先下載至手機儲存後,在使用 DexClassLoader 動態載入 class (作法請參考下篇 使用 DexClassLoader 動態載入 DEX)