รันไทม์ของ 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
JNI ของ ART ค่อนข้างเข้มงวดกว่าของ Dalvik วิธีนี้เป็นความคิดที่ดีอย่างยิ่ง เพื่อใช้โหมด CheckJNI เพื่อหาปัญหาที่พบบ่อย หากแอปใช้ C/C++ คุณควรอ่านบทความต่อไปนี้
การแก้ไขข้อบกพร่อง Android JNI กับ CheckJNI
กำลังตรวจสอบรหัส JNI เกี่ยวกับปัญหาการเก็บรวบรวมข้อมูลขยะ
ตัวรวบรวมการคัดลอก (CC) พร้อมกันอาจย้ายวัตถุในหน่วยความจำเพื่อบีบอัด หากคุณใช้โค้ด C/C++ โปรดอย่า ดำเนินการที่เข้ากันไม่ได้กับ GC ที่บีบอัด เราได้ปรับปรุง CheckJNI เพื่อระบุปัญหาที่อาจเกิดขึ้น (ตามที่อธิบายไว้ใน JNI การเปลี่ยนแปลงข้อมูลอ้างอิงในท้องถิ่นใน 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 แสดงข้อผิดพลาดในบางกรณีที่ 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)
ART ยังบันทึกข้อผิดพลาด (ดูได้ใน Logcat) หาก RegisterNatives
คือ
โดยไม่มีเมธอด:
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
นอกจากนี้ ฟังก์ชัน JNI จะ GetFieldID()
และ
ตอนนี้ GetStaticFieldID()
ส่ง NoSuchFieldError
ได้ถูกต้อง
แทนที่จะแสดงผลเป็น Null ในทำนองเดียวกัน GetMethodID()
และ
ตอนนี้ GetStaticMethodID()
ส่ง NoSuchMethodError
ได้ถูกต้อง
ซึ่งอาจทำให้ CheckJNI ทำงานไม่สำเร็จเนื่องจากข้อยกเว้นที่ไม่ได้รับการจัดการ
ข้อยกเว้นที่จะเกิดกับผู้โทร Java ของโค้ดเนทีฟ จึงทำให้
โดยเฉพาะกับการทดสอบแอปที่เข้ากันได้กับ ART ด้วยโหมด CheckJNI
ART คาดหวังว่าผู้ใช้เมธอด JNI CallNonvirtual...Method()
(เช่น CallNonvirtualVoidMethod()
) เพื่อใช้การประกาศของเมธอด
ไม่ใช่คลาสย่อยตามที่ JNI กำหนดไว้
การป้องกันปัญหาเกี่ยวกับขนาดสแต็ก
Dalvik มีสแต็กแยกต่างหากสำหรับโค้ดเนทีฟและโค้ด Java ที่มี Java เริ่มต้น
ขนาดสแต็ก 32 KB และขนาดสแต็กเริ่มต้นคือ 1 MB ART มีเอกภาพ
เพื่อย่านที่ดีขึ้น โดยปกติ สแต็ก ART Thread
ควรมีขนาดประมาณเท่ากับสำหรับ Dalvik แต่ถ้าคุณ
ตั้งค่าขนาดสแต็ก คุณอาจต้องกลับไปดูค่าเหล่านั้นสําหรับแอปที่ทำงานใน
ART
- ใน Java ให้ตรวจสอบการเรียกใช้ตัวสร้าง
Thread
ที่ระบุสแต็กที่ชัดเจน ขนาด เช่น คุณจะต้องเพิ่มขนาดหากมีStackOverflowError
- ใน C/C++ ให้ตรวจสอบการใช้
pthread_attr_setstack()
และpthread_attr_setstacksize()
สำหรับชุดข้อความที่เรียกใช้โค้ด Java ด้วย JNI ต่อไปนี้เป็นตัวอย่างของข้อผิดพลาดที่บันทึกไว้เมื่อแอปพยายามโทรหา JNIAttachCurrentThread()
เมื่อขนาด 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 เวอร์ชันก่อนหน้ามีปัญหาเกี่ยวกับ
ดังนั้นให้ใช้ Mockito เวอร์ชันที่อัปเดตแล้วเมื่อทดสอบกับ ART
การแก้ปัญหาการคอมไพล์ AOT
การคอมไพล์ Java Ahead-Of-Time (AOT) ของ ART ควรใช้งานได้กับ Java มาตรฐานทั้งหมด
โค้ด ดำเนินการคอมไพล์โดย ART
เครื่องมือ dex2oat
หากพบปัญหาที่เกี่ยวข้องกับ
dex2oat
ณ เวลาที่ติดตั้ง โปรดแจ้งให้เราทราบ (ดูการรายงานปัญหาเกี่ยวกับ) เพื่อให้เราแก้ไขได้อย่างรวดเร็ว
ให้มากที่สุด ปัญหา 2 ข้อที่ควรทราบมีดังนี้
- ART ยืนยันไบต์โค้ดในเวลาติดตั้งได้มากกว่า Dalvik ที่ทำ โค้ดที่เครื่องมือสร้างของ Android สร้างขึ้นจะไม่สามารถใช้งานได้ อย่างไรก็ตาม เครื่องมือหลังการประมวลผล (โดยเฉพาะเครื่องมือที่สร้างความสับสน) อาจสร้าง ไฟล์ที่ไม่ถูกต้องซึ่ง Dalvik ยอมรับแต่ ART ปฏิเสธ เราคือ ทำงานร่วมกับผู้ให้บริการเครื่องมือเพื่อค้นหาและแก้ไขปัญหาดังกล่าว ในหลายกรณี การได้รับ เครื่องมือเวอร์ชันล่าสุดและการสร้างไฟล์ DEX ใหม่สามารถแก้ปัญหาเหล่านี้ได้ ปัญหา
- ปัญหาทั่วไปบางอย่างที่เครื่องมือตรวจสอบ ART แจ้งว่าไม่เหมาะสม ได้แก่
- ขั้นตอนการควบคุมไม่ถูกต้อง
- ไม่สมดุล
monitorenter
/monitorexit
- ขนาดรายการประเภทพารามิเตอร์ความยาว 0
- แอปบางแอปมีทรัพยากร Dependency ในไฟล์
.odex
ที่ติดตั้งไว้ รูปแบบใน/system/framework
,/data/dalvik-cache
หรือ ในไดเรกทอรีเอาต์พุตที่เพิ่มประสิทธิภาพแล้วของDexClassLoader
เหล่านี้ ตอนนี้เป็นไฟล์ ELF ไม่ใช่ไฟล์ DEX ในรูปแบบขยาย ขณะที่ ART พยายาม ให้เป็นไปตามการตั้งชื่อและกฎการล็อกเดียวกันกับ Dalvik แอปไม่ควร เกี่ยวกับรูปแบบไฟล์ รูปแบบดังกล่าวอาจเปลี่ยนแปลงได้โดยไม่ต้องแจ้งให้ทราบหมายเหตุ: ใน Android 8.0 (API ระดับ 26) และ ไดเรกทอรีเอาต์พุตที่เพิ่มประสิทธิภาพ
DexClassLoader
แล้ว เลิกใช้งานแล้ว สําหรับข้อมูลเพิ่มเติม โปรดดูเอกสารประกอบสําหรับDexClassLoader()
เครื่องมือสร้างขึ้นมา
การรายงานปัญหา
หากพบปัญหาที่ไม่ได้มาจากปัญหา JNI ของแอป โปรดรายงาน
ผ่านเครื่องมือติดตามปัญหาโครงการโอเพนซอร์ส Android ที่ https://code.google.com/p/android/issues/list
ใส่ "adb bugreport"
และลิงก์ไปยังแอปใน Google
Play Store (หากมี) หากทำได้ ให้แนบ APK ที่ทำให้เกิดซ้ำ
ถึงปัญหา โปรดทราบว่าปัญหา (รวมถึงไฟล์แนบ) เป็นข้อมูลสาธารณะ
มองเห็นได้