Android çalışma zamanında (ART) uygulama davranışını doğrulama

Android çalışma zamanı (ART), Android 5.0 (API düzeyi 21) ve sonraki sürümleri çalıştıran cihazlar için varsayılan çalışma zamanıdır. Bu çalışma zamanı, Android platform ve uygulamaların performansını ve sorunsuzlığını artıran bazı özellikler sunar. ART ile tanışın başlıklı makalede ART'ın yeni özellikleri hakkında daha fazla bilgi edinebilirsiniz.

Ancak, Dalvik'te çalışan bazı teknikler ART'ta işe yaramaz. Bu dokümanda, mevcut bir uygulamayı ART ile uyumlu olacak şekilde taşırken dikkat edilmesi gereken noktalar hakkında bilgi verilmektedir. Çoğu uygulama ART ile çalışırken çalışır.

Atık toplama (GC) sorunlarını çözme

Dalvik'te uygulamalar, çöp toplama (GC) isteminde bulunmak için açıkça System.gc() çağrısı yapmayı genellikle faydalı bulur. Bu işlem, özellikle GC_FOR_ALLOC türü durumları önlemek veya parçalanmayı azaltmak için çöp toplama işlemi gerçekleştiriyorsanız ART ile çok daha az gerekli olacaktır. System.getProperty("java.vm.version") yöntemini çağırarak hangi çalışma zamanının kullanılmakta olduğunu doğrulayabilirsiniz. ART kullanılıyorsa mülkün değeri "2.0.0" veya daha yüksek olur.

ART, Java yığınını eşzamanlı olarak sıkıştıran Eşzamanlı Kopyalama (CC) toplayıcısı kullanır. Bu nedenle, sıkıştırma GC ile uyumlu olmayan teknikler kullanmaktan kaçınmalısınız (örneğin, işaretçileri nesne örneği verilerine kaydetmek gibi). Bu, özellikle Java Yerel Arayüzü (JNI) kullanan uygulamalar için önemlidir. Daha fazla bilgi için JNI Sorunlarını Önleme başlıklı makaleyi inceleyin.

JNI sorunlarını önleme

ART'nin JNI'si Dalvik'inkinden biraz daha katıdır. Yaygın sorunları yakalamak için CheckJNI modunu kullanmak özellikle iyi bir fikirdir. Uygulamanız C/C++ kodunu kullanıyorsa aşağıdaki makaleyi incelemeniz gerekir:

CheckJNI ile Android JNI'de Hata Ayıklama

Çöp toplama sorunları için JNI kodunu kontrol etme

Eşzamanlı Kopyalama (CC) toplayıcısı, sıkıştırma için nesneleri bellekte taşıyabilir. C/C++ kodu kullanıyorsanız sıkıştırma GC ile uyumlu olmayan işlemler gerçekleştirmeyin. Bazı potansiyel sorunları belirlemek için CheckJNI'yi geliştirdik (ICS'deki JNI Yerel Referans Değişiklikleri'nde açıklandığı gibi).

Özellikle de dikkat edilmesi gereken alanlardan biri Get...ArrayElements() ve Release...ArrayElements() işlevlerinin kullanımıdır. Sıkıştırılmamış GC'ye sahip çalışma zamanlarında, Get...ArrayElements() işlevleri genellikle dizi nesnesini destekleyen gerçek belleğe bir referans döndürür. Döndürülen dizi öğelerinden birinde değişiklik yaparsanız dizi nesnesinin kendisi değiştirilir (ve Release...ArrayElements() bağımsız değişkenleri genellikle göz ardı edilir). Ancak GC sıkıştırma kullanılıyorsa Get...ArrayElements() işlevleri, belleğin bir kopyasını döndürebilir. GC'yi sıkıştırma kullanılırken referansı yanlış kullanırsanız, bellek bozulması veya başka sorunlara neden olabilir. Örneğin:

  • Döndürülen dizi öğelerinde değişiklik yaparsanız yaptığınız değişikliklerin temel dizi nesnesine doğru şekilde geri kopyalandığından emin olmak için işiniz bittiğinde uygun Release...ArrayElements() işlevini çağırmanız gerekir.
  • Bellek dizisi öğelerini serbest bıraktığınızda, yaptığınız değişikliklere bağlı olarak uygun modu kullanmanız gerekir:
    • Dizi öğelerinde herhangi bir değişiklik yapmadıysanız JNI_ABORT modunu kullanın. Bu mod, değişiklikleri temel dizi nesnesine kopyalamadan belleği serbest bırakır.
    • Dizide değişiklik yaptıysanız ve referansa artık ihtiyacınız yoksa 0 kodunu kullanın (dizi nesnesi güncellenir ve belleğin kopyası serbest bırakılır).
    • Dizide, uygulamak istediğiniz değişiklikler yaptıysanız ve dizinin kopyasını tutmak istiyorsanız JNI_COMMIT işlevini kullanın (temel dizi nesnesini günceller ve kopyayı tutar).
  • Release...ArrayElements() çağırırken Get...ArrayElements() tarafından başlangıçta döndürülen işaretçinin aynısını döndürün. Örneğin, orijinal işaretçiyi artırmak (döndürülen dizi öğelerini taramak için) ve ardından artan işaretçiyi Release...ArrayElements() öğesine geçirmek güvenli değildir. Değiştirilmiş bu işaretçinin iletilmesi yanlış belleğin serbest bırakılmasına ve bunun sonucunda bellek bozulmasına yol açabilir.

Hata işleme

ART JNI, Dalvik'in bunu yapmadığı bazı durumlarda hatalar ortaya çıkarmaktadır. (Bu tür durumların birçoğunu tekrar CheckJNI ile test yaparak yakalayabilirsiniz.)

Örneğin, RegisterNatives mevcut olmayan bir yöntemle çağrılırsa (yöntem ProGuard gibi bir araç tarafından kaldırılmış olabilir) ART artık gerektiği gibi NoSuchMethodError gönderir:

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 herhangi bir yöntem olmadan çağrılırsa ART, bir hata kaydeder (logcat'te görünür):

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

Ayrıca, JNI işlevleri GetFieldID() ve GetStaticFieldID() artık yalnızca null döndürmek yerine NoSuchFieldError değerini düzgün bir şekilde döndürür. Benzer şekilde, GetMethodID() ve GetStaticMethodID() artık NoSuchMethodError düzgün bir şekilde atıyor. Bu, işlenmemiş istisnalar veya yerel kodun Java çağrılarını yapan istisnaları nedeniyle CheckJNI hatalarına yol açabilir. Bu nedenle, ART uyumlu uygulamaların CheckJNI moduyla test edilmesi özellikle önemlidir.

ART, JNI CallNonvirtual...Method() yöntemlerini (CallNonvirtualVoidMethod() gibi) kullananların, JNI spesifikasyonunun gerektirdiği şekilde alt sınıfı değil, yöntemin beyan sınıfını kullanmasını bekler.

Yığın boyutu sorunlarını önleme

Dalvik, yerel ve Java kodu için ayrı yığınlara sahipti. Varsayılan Java yığın boyutu: 32 KB, varsayılan yerel yığın boyutu ise 1 MB'tı. ART, daha iyi bir konum için birleştirilmiş bir yığına sahiptir. Normalde, ART Thread yığın boyutu, Dalvik ile yaklaşık olarak aynı olmalıdır. Ancak, yığın boyutlarını açıkça ayarlarsanız ART'da çalışan uygulamalar için bu değerleri tekrar gözden geçirmeniz gerekebilir.

  • Java'da açık bir yığın boyutu belirten Thread oluşturucusuna yapılan çağrıları inceleyin. Örneğin, StackOverflowError olursa boyutu artırmanız gerekir.
  • C/C++'ta JNI aracılığıyla Java kodu da çalıştıran iş parçacıkları için pthread_attr_setstack() ve pthread_attr_setstacksize() kullanımını inceleyin. İş parçacığı boyutu çok küçük olduğunda bir uygulama JNI AttachCurrentThread() yöntemini çağırmaya çalıştığında günlüğe kaydedilen hata örneğini burada bulabilirsiniz:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

Nesne modeli değişiklikleri

Dalvik, yanlış bir şekilde alt sınıfların pakete özel yöntemleri geçersiz kılmasına izin verdi. ART aşağıdaki durumlarda uyarı verir:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

Farklı bir pakette bir sınıfın yöntemini geçersiz kılmak istiyorsanız yöntemi public veya protected olarak bildirin.

Object artık özel alanlara sahip. Sınıf hiyerarşilerindeki alanları yansıtan uygulamalar, Object alanlarına bakmaya çalışmamaya dikkat etmelidir. Örneğin, bir serileştirme çerçevesi kapsamında sınıf hiyerarşisini yineliyorsanız

Class.getSuperclass() == java.lang.Object.class

yöntemi null değerini döndürene kadar devam eder.

InvocationHandler.invoke() proxy'si artık boş dizi yerine hiç bağımsız değişken yoksa null alıyor. Bu davranış daha önce belgelenmiş olsa da Dalvik'te doğru şekilde işlenmemiştir. Mockito'nun önceki sürümlerinde bu konuda sorun yaşandığından ART ile test ederken güncellenmiş bir Mockito sürümünü kullanın.

AOT derleme sorunlarını düzeltme

ART'ın Ahead-Of-Time (AOT) Java derlemesi tüm standart Java kodları için çalışır. Derleme, ART'ın dex2oat aracı tarafından gerçekleştirilir. Yükleme sırasında dex2oat ile ilgili herhangi bir sorunla karşılaşırsanız bunu mümkün olan en kısa sürede düzeltebilmemiz için bize bildirin (Sorunları Bildirme bölümüne bakın). Dikkat edilmesi gereken birkaç sorun:

  • ART, yükleme sırasında Dalvik'ten daha sıkı bayt kodu doğrulaması gerçekleştirir. Android derleme araçları tarafından üretilen kodlar yeterlidir. Bununla birlikte, bazı son işleme araçları (özellikle kod karartma gerçekleştiren araçlar) Dalvik tarafından tolere edilen ancak ART tarafından reddedilen geçersiz dosyalar oluşturabilir. Bu tür sorunları bulup düzeltmek için araç tedarikçileriyle birlikte çalışıyoruz. Çoğu durumda, araçlarınızın en yeni sürümlerini almak ve DEX dosyalarını yeniden oluşturmak bu sorunları çözebilir.
  • ART doğrulayıcısı tarafından işaretlenen bazı tipik sorunlar şunlardır:
    • geçersiz kontrol akışı
    • dengesiz monitorenter/monitorexit
    • 0 uzunluklu parametre türü liste boyutu
  • Bazı uygulamaların /system/framework, /data/dalvik-cache veya DexClassLoader uygulamasının optimize edilmiş çıkış dizininde yüklü .odex dosya biçimine bağımlılığı var. Bu dosyalar artık ELF dosyaları olup DEX dosyalarının genişletilmiş bir biçimi değildir. ART, Dalvik ile aynı adlandırma ve kilitleme kurallarına uymaya çalışsa da uygulamaların dosya biçimine bağlı olmaması gerekir. Biçim, bildirimde bulunulmadan değiştirilebilir.

    Not: Android 8.0 (API düzeyi 26) ve sonraki sürümlerde, DexClassLoader için optimize edilmiş çıkış dizini kullanımdan kaldırılmıştır. Daha fazla bilgi için DexClassLoader() oluşturucunun dokümanlarına bakın.

Raporlama sorunları

Uygulama JNI sorunlarından kaynaklanmayan bir sorunla karşılaşırsanız https://code.google.com/p/android/issues/list adresindeki Android Açık Kaynak Proje Sorun İzleyicisi aracılığıyla bu sorunları bildirin. Varsa bir "adb bugreport" ve Google Play Store'a uygulamanın bağlantısını ekleyin. Aksi takdirde, mümkünse sorunu yeniden oluşturan bir APK ekleyin. Sorunların (ekler dahil) herkes tarafından görülebildiğini unutmayın.