Android 執行階段 (ART) 是搭載 Android 5.0 (API 級別 21) 以上版本的裝置預設執行階段。這個執行階段提供一些功能,可以改善 Android 平台和應用程式的效能和順暢度。如要進一步瞭解 ART 的新功能,請參閱「ART 簡介」一文。
不過,部分在 Dalvik 中運作的技術不適用於 ART。這份文件可讓您瞭解遷移現有應用程式以與 ART 相容的內容時,需要留意的事項。大多數應用程式應該在與 ART 執行時都能正常運作。
解決垃圾收集 (GC) 問題
在 Dalvik 下,應用程式明確呼叫 System.gc()
來提示垃圾收集 (GC) 會很有幫助。使用 ART 時,這應該比較不需要,尤其是在叫用垃圾收集來防止 GC_FOR_ALLOC
類型發生或減少片段化時。您可以呼叫 System.getProperty("java.vm.version")
來確認使用中的執行階段。如果使用 ART,則屬性值為 "2.0.0"
以上。
ART 會使用並行複製 (CC) 收集器,並行壓縮 Java 堆積。因此,請避免使用與壓縮 GC 不相容的技巧 (例如儲存物件執行個體資料的指標)。這對於使用 Java Native Interface (JNI) 的應用程式而言特別重要。詳情請參閱「預防 JNI 問題」。
防止 JNI 問題
ART 的 JNI 比 Dalvik 還嚴格。特別適合使用 CheckJNI 模式找出常見問題。如果應用程式使用 C/C++ 程式碼,請詳閱下列文章:
使用 CheckJNI 對 Android JNI 進行偵錯
檢查 JNI 程式碼是否有垃圾收集問題
並行複製 (CC) 收集器可能會移動記憶體中的物件以進行壓縮。如果您使用 C/C++ 程式碼,請勿執行與壓縮 GC 不相容的作業。我們增強了 CheckJNI 來找出一些潛在問題 (請參閱 ICS 中的 JNI 本機參考資料變更所述)。
值得特別留意的是,使用 Get...ArrayElements()
和 Release...ArrayElements()
函式。在不含壓縮 GC 的執行階段中,Get...ArrayElements()
函式通常會傳回支援陣列物件的實際記憶體參照。如果您變更其中一個傳回的陣列元素,陣列物件本身就會發生變更 (系統通常會忽略 Release...ArrayElements()
的引數)。不過,如果正在使用壓縮 GC,Get...ArrayElements()
函式可能會回傳記憶體副本。如果您在壓縮 GC 時濫用參照,可能會導致記憶體損毀或其他問題。例如:
- 如果您對傳回的陣列元素進行任何變更,則必須在完成後呼叫適當的
Release...ArrayElements()
函式,以確保您所做的變更能正確複製到基礎陣列物件。 - 釋出記憶體陣列元素時,您必須根據所做的變更採用適當的模式:
- 如果您沒有變更陣列元素,請使用
JNI_ABORT
模式來釋出記憶體,而不會將變更複製回基礎陣列物件。 - 如果您已變更陣列,且不再需要參照,請使用程式碼
0
(這會更新陣列物件並釋放記憶體副本)。 - 如果您已變更要修訂的陣列,且想要保留陣列的複本,請使用
JNI_COMMIT
(這會更新基礎陣列物件並保留副本)。
- 如果您沒有變更陣列元素,請使用
- 當您呼叫
Release...ArrayElements()
時,傳回Get...ArrayElements()
原本傳回的指標。舉例來說,增加原始指標並掃描傳回的陣列元素,然後將遞增指標傳遞至Release...ArrayElements()
並不安全。傳遞這個已修改的指標可能會導致系統釋放錯誤的記憶體,進而造成記憶體損毀。
處理錯誤
ART 的 JNI 在一些 Dalvik 無效的情況下擲回錯誤。(然後再次使用 CheckJNI 進行測試,可以找出許多這類情況)。
舉例來說,如果使用不存在的方法呼叫 RegisterNatives
(可能是因為 ProGuard 等工具已移除該方法),ART 現在會正確擲回 NoSuchMethodError
:
08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main 08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError: no static or non-static method "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I" 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.nativeLoad(Native Method) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.doLoad(Runtime.java:421) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.Runtime.loadLibrary(Runtime.java:362) 08-12 17:09:41.082 13823 13823 E AndroidRuntime: at java.lang.System.loadLibrary(System.java:526)
如果在沒有方法的情況下呼叫 RegisterNatives
,ART 也會記錄錯誤 (會顯示在 logcat 中):
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
此外,JNI 函式 GetFieldID()
和 GetStaticFieldID()
現在可正確擲回 NoSuchFieldError
,而非只傳回空值。同樣地,GetMethodID()
和 GetStaticMethodID()
現在可正確擲回 NoSuchMethodError
。這可能會導致未處理的例外狀況,或原生程式碼向 Java 呼叫端擲回的例外狀況,導致 CheckJNI 失敗。因此,使用 CheckJNI 模式測試與 ART 相容的應用程式特別重要。
ART 預期 JNI CallNonvirtual...Method()
方法 (例如 CallNonvirtualVoidMethod()
) 的使用者會使用方法的宣告類別,而非 JNI 規格要求的子類別。
避免堆疊大小問題
Dalvik 有不同的原生和 Java 程式碼堆疊,其預設 Java 堆疊大小為 32KB,預設原生堆疊大小為 1 MB。ART 有整合式堆疊,以提升位置。一般來說,ART Thread
堆疊大小應與 Dalvik 大致相同。不過,如果您明確設定堆疊大小,可能需要針對在 ART 中執行的應用程式重新查看這些值。
- 在 Java 中,查看對指定明確堆疊大小的
Thread
建構函式的呼叫。舉例來說,如果發生StackOverflowError
,就必須增加大小。 - 在 C/C++ 中,檢查也會透過 JNI 執行 Java 程式碼的執行緒使用
pthread_attr_setstack()
和pthread_attr_setstacksize()
的情形。以下是應用程式在 pthread 大小太小時,嘗試呼叫 JNIAttachCurrentThread()
時記錄的錯誤示例:F/art: art/runtime/thread.cc:435] Attempt to attach a thread with a too-small stack (16384 bytes)
物件模型變更
Dalvik 錯誤地允許子類別覆寫套件私人方法。在以下情況下,ART 會發出警告:
Before Android 4.1, method void com.foo.Bar.quux() would have incorrectly overridden the package-private method in com.quux.Quux
如要覆寫其他套件中的類別方法,請將該方法宣告為 public
或 protected
。
「Object
」現在有私人欄位。如果在類別階層中反映欄位,應用程式應謹慎處理,不要嘗試查看 Object
的欄位。舉例來說,如果您要在序列化架構中疊代類別階層,請停止
Class.getSuperclass() == java.lang.Object.class
,直到方法傳回 null
為止。
如果沒有任何引數,而不是空白陣列,Proxy InvocationHandler.invoke()
現在會收到 null
。系統先前已記錄過這個行為,但在 Dalvik 中並未正確處理。舊版 Mockito 會遇到這個問題,因此透過 ART 進行測試時,請使用更新的 Mockito 版本。
修正 AOT 編譯問題
ART 的預先 (AOT) Java 編譯應適用於所有標準 Java 程式碼。編譯作業是由 ART 的 dex2oat
工具執行;如果您在安裝時遇到任何與 dex2oat
相關的問題,請告訴我們 (請參閱「回報問題」一節),以便我們盡快修正。請注意以下幾個問題:
- 相較於 Dalvik,ART 在安裝時執行的位元碼驗證更加嚴格。Android 建構工具產生的程式碼應該沒有問題。不過,部分後續處理工具 (尤其是執行模糊處理的工具) 可能會產生 Dalvik 容許但遭到 ART 拒絕的無效檔案。我們一直與工具供應商合作,以找出並修正這類問題。在許多情況下,取得最新版工具並重新產生 DEX 檔案,即可修正問題。
- ART 驗證器標記的一些常見問題包括:
- 控制流程無效
- 不平衡:
monitorenter
/monitorexit
- 0 長度參數類型清單大小
- 某些應用程式的依附元件依附於
/system/framework
、/data/dalvik-cache
或DexClassLoader
的最佳化輸出目錄中,已安裝的.odex
檔案格式。這些檔案現在是 ELF 檔案,而不是擴展的 DEX 檔案。雖然 ART 會嘗試遵循與 Dalvik 相同的命名和鎖定規則,但應用程式不應依附於檔案格式;格式如有變更,恕不另行通知。注意:在 Android 8.0 (API 級別 26) 以上版本中,
DexClassLoader
最佳化輸出目錄已淘汰。詳情請參閱DexClassLoader()
建構函式的說明文件。
報表問題
如果您遇到並非因應用程式 JNI 問題而產生的任何問題,請透過 https://code.google.com/p/android/issues/list 的 Android 開放原始碼計畫 Issue Tracker 回報問題。加入 "adb bugreport"
和 Google Play 商店中的應用程式連結 (如有)。否則,如果可以,請附加可重現問題的 APK。請注意,問題 (包括附件) 會公開顯示。