2014年2月19日

LinearAlloc exceeded capacity Problem


在開發 Android App 的過程中,專案會因為功能需求引入一些 opensource 套件包、lib、jar等資源,當專案日益龐大,就有可能發生 LinearAlloc exceeded capacity 的問題。本篇就來簡單介紹一下原因以及可行的解決方法
首先,先簡單介紹 LinearAlloc

LinearAlloc (線性分配器)

  1. source code:android_src/dalvik/vm/ 下 LinearAlloc.h 和 LinearAlloc.c
  2. 目的:主要用来管理 Dalvik 中 class 加載時的内存,使其簡單、快速地分配内存。簡單來說就是讓app在執行時,減少系統內存的佔用。
  3. 屬於 Android Running 層,由 Dalvik Virtual Machine 執行

Dalvik 與 DEX 文件檔

  1. Android 上所提供的 ByteCode 虛擬器
  2. 目標:
    (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) 為了安全需求,執行中的行程不能修改共享的程式碼
  3. 核心的函式庫主要是承襲 Open Source 的 Java SE 實作 Apache Harmony 而來,並基於 Open Source 中相關 OpenSSL, zlib 與 ICU(International Components for Unicode) 的計畫成果
    ※ 一般虛擬器的設計,會在應用程式啟動時,動態的從儲存裝置讀取並解壓縮個別的 Classes 到記憶體中,在沒有經過適度優化的架構下,每個獨立的虛擬器行程,都會包含自己一份的相關 Classes 記憶體空間
  4. 基於上述目標, Dalvik 在設計時,作了以下的決定:
    (1) 可以把多個 Classes 檔案整合到一個 DEX 檔案中
    (2) DEX 檔案會以唯讀方案載入到記憶體中,並跨行程共享
    (3) 因應支援的系統架構,調整 Byte Ordering 與 Word Alignment
    (4) ByteCode 驗證對所有的 Classes 都是必要的,且會儘可能進行事先的驗證加速系統效率
    (5) 對於需修改 ByteCode 的最佳化動作,會在執行前完成

APK文件檔

  1. 屬於一種壓縮檔案,可用 zip 解壓縮
  2. 包含:
    (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

  1. 原因: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)

References