بررسی رفتار برنامه در زمان اجرا اندروید (ART)

زمان اجرا Android (ART) زمان اجرا پیش‌فرض برای دستگاه‌های دارای Android نسخه 5.0 (سطح API 21) و بالاتر است. این زمان اجرا تعدادی ویژگی را ارائه می دهد که عملکرد و روان بودن پلتفرم و برنامه های اندروید را بهبود می بخشد. می توانید اطلاعات بیشتری در مورد ویژگی های جدید 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) استفاده می کند که همزمان پشته جاوا را فشرده می کند. به همین دلیل، باید از استفاده از تکنیک هایی که با فشرده سازی GC ناسازگار هستند (مانند ذخیره اشاره گرها در داده های نمونه شی) اجتناب کنید. این به ویژه برای برنامه هایی که از رابط بومی جاوا (JNI) استفاده می کنند بسیار مهم است. برای اطلاعات بیشتر، به پیشگیری از مشکلات JNI مراجعه کنید.

پیشگیری از مسائل JNI

JNI ART تا حدودی سختگیرتر از Dalvik است. استفاده از حالت CheckJNI برای یافتن مشکلات رایج ایده خوبی است. اگر برنامه شما از کد C/C++ استفاده می کند، باید مقاله زیر را مرور کنید:

اشکال زدایی اندروید JNI با CheckJNI

بررسی کد JNI برای مشکلات جمع آوری زباله

گردآورنده کپی همزمان (CC) ممکن است اشیاء را در حافظه برای فشرده سازی حرکت دهد. اگر از کد C/C++ استفاده می کنید، عملیاتی را انجام ندهید که با فشرده سازی GC ناسازگار است. ما CheckJNI را برای شناسایی برخی مشکلات بالقوه (همانطور که در JNI Local Reference Changes in ICS توضیح داده شده است) تقویت کرده ایم.

یکی از مناطقی که به طور خاص باید مراقب آن بود، استفاده از توابع 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() امن نیست. عبور از این نشانگر اصلاح شده می تواند باعث آزاد شدن حافظه اشتباه و در نتیجه خراب شدن حافظه شود.

رسیدگی به خطا

JNI ART در تعدادی از مواردی که دالویک این کار را نمی کند، خطا می کند. (یک بار دیگر، می توانید با آزمایش با 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() اکنون به جای اینکه به سادگی null را برگردانند NoSuchFieldError به درستی پرتاب می کنند. به طور مشابه، GetMethodID() و GetStaticMethodID() اکنون NoSuchMethodError به درستی پرتاب می کنند. این می تواند منجر به شکست CheckJNI به دلیل استثناهای کنترل نشده یا استثناهایی که برای فراخواننده های جاوا کد بومی ایجاد می شود، شود. این امر اهمیت ویژه ای برای آزمایش برنامه های سازگار با ART با حالت CheckJNI می کند.

ART از کاربران متدهای JNI CallNonvirtual...Method() (مانند CallNonvirtualVoidMethod() ) انتظار دارد که طبق مشخصات JNI از کلاس اعلان متد استفاده کنند، نه یک کلاس فرعی.

جلوگیری از مشکلات اندازه پشته

Dalvik دارای پشته های جداگانه برای کدهای بومی و جاوا بود، با اندازه پیش فرض پشته جاوا 32 کیلوبایت و اندازه پشته اصلی پیش فرض 1 مگابایت. ART یک پشته یکپارچه برای موقعیت بهتر دارد. به طور معمول، اندازه پشته ART Thread باید تقریباً مشابه Dalvik باشد. با این حال، اگر به صراحت اندازه‌های پشته را تنظیم کنید، ممکن است لازم باشد آن مقادیر را برای برنامه‌های در حال اجرا در ART بازبینی کنید.

  • در جاوا، فراخوانی‌های سازنده Thread را بررسی کنید که اندازه پشته صریح را مشخص می‌کنند. برای مثال، اگر StackOverflowError رخ داد، باید اندازه را افزایش دهید.
  • در C/C++، استفاده از pthread_attr_setstack() و pthread_attr_setstacksize() برای رشته هایی که کد جاوا را نیز از طریق JNI اجرا می کنند، مرور کنید. در اینجا مثالی از خطای ثبت شده هنگام تلاش یک برنامه برای فراخوانی JNI AttachCurrentThread() در زمانی که اندازه pthread خیلی کوچک است آورده شده است:
    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 را برگرداند.

پروکسی InvocationHandler.invoke() اکنون اگر آرگومان به جای آرایه خالی وجود نداشته باشد null دریافت می کند. این رفتار قبلاً مستند شده بود، اما به درستی در Dalvik استفاده نشده بود. نسخه‌های قبلی Mockito با این مشکل مواجه هستند، بنابراین هنگام آزمایش با ART از نسخه به‌روز شده Mockito استفاده کنید.

رفع مشکلات کامپایل AOT

کامپایل جاوا Ahead-Of-Time (AOT) ART باید برای همه کدهای استاندارد جاوا کار کند. کامپایل توسط ابزار dex2oat ART انجام می شود. اگر در زمان نصب با مشکلی در ارتباط با dex2oat مواجه شدید، به ما اطلاع دهید (به گزارش مشکلات مراجعه کنید) تا بتوانیم در اسرع وقت آنها را برطرف کنیم. چند موضوع قابل توجه:

  • ART در زمان نصب بایت کد دقیق تری نسبت به Dalvik انجام می دهد. کد تولید شده توسط ابزارهای ساخت اندروید باید خوب باشد. با این حال، برخی از ابزارهای پس پردازش (به ویژه ابزارهایی که مبهم سازی را انجام می دهند) ممکن است فایل های نامعتبری را تولید کنند که توسط Dalvik قابل تحمل است اما توسط ART رد شده است. ما با فروشندگان ابزار برای یافتن و رفع چنین مشکلاتی کار کرده ایم. در بسیاری از موارد، دریافت آخرین نسخه ابزارهای خود و بازسازی فایل های DEX می تواند این مشکلات را برطرف کند.
  • برخی از مشکلات معمولی که توسط تأیید کننده ART پرچم گذاری می شوند عبارتند از:
    • جریان کنترل نامعتبر
    • monitorenter نامتعادل / monitorexit
    • اندازه لیست نوع پارامتر با طول 0
  • برخی از برنامه ها به فرمت فایل .odex نصب شده در /system/framework ، /data/dalvik-cache یا در فهرست خروجی بهینه شده DexClassLoader وابستگی دارند. این فایل ها اکنون فایل های ELF هستند و فرم توسعه یافته فایل های DEX نیستند. در حالی که ART سعی می کند از قوانین نامگذاری و قفل کردن مشابه Dalvik پیروی کند، برنامه ها نباید به فرمت فایل وابسته باشند. قالب ممکن است بدون اطلاع قبلی تغییر کند.

    توجه: در اندروید 8.0 (سطح API 26) و بالاتر، فهرست خروجی بهینه شده DexClassLoader منسوخ شده است. برای اطلاعات بیشتر، به مستندات سازنده DexClassLoader() مراجعه کنید.

گزارش مشکلات

اگر با مشکلاتی مواجه شدید که به دلیل مشکلات برنامه JNI نیست، آنها را از طریق ردیاب مشکل پروژه منبع باز Android در https://code.google.com/p/android/issues/list گزارش دهید. در صورت موجود بودن "adb bugreport" و پیوندی به برنامه در فروشگاه Google Play اضافه کنید. در غیر این صورت، در صورت امکان، یک APK که مشکل را بازتولید می کند، پیوست کنید. توجه داشته باشید که مشکلات (از جمله پیوست ها) به صورت عمومی قابل مشاهده هستند.