วิธีจัดการ: java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize () หมดเวลาหลังจากข้อผิดพลาด 10 วินาที?


167

เราเห็นจำนวนของTimeoutExceptionsในและGcWatcher.finalize, BinderProxy.finalize PlainSocketImpl.finalize90 +% ของพวกเขาเกิดขึ้นใน Android 4.3 เราได้รับรายงานเรื่องนี้จาก Crittercism จากผู้ใช้ในฟิลด์

ป้อนคำอธิบายรูปภาพที่นี่

ข้อผิดพลาดคือรูปแบบของ: " com.android.internal.BinderInternal$GcWatcher.finalize() timed out after 10 seconds"

java.util.concurrent.TimeoutException: android.os.BinderProxy.finalize() timed out after 10 seconds
at android.os.BinderProxy.destroy(Native Method)
at android.os.BinderProxy.finalize(Binder.java:459)
at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:187)
at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:170)
at java.lang.Thread.run(Thread.java:841)

จนถึงตอนนี้เรายังไม่มีโชคในการทำซ้ำปัญหาในบ้านหรือหาสิ่งที่อาจทำให้เกิด

ความคิดใดที่ทำให้เกิดสิ่งนี้ มีความคิดวิธีการแก้ไขข้อบกพร่องนี้และค้นหาว่าส่วนใดของแอพทำให้เกิดปัญหานี้ อะไรก็ตามที่ให้ความกระจ่างเกี่ยวกับปัญหาจะช่วยได้

Stacktraces เพิ่มเติม:

1   android.os.BinderProxy.destroy  
2   android.os.BinderProxy.finalize Binder.java, line 482
3   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
4   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
5   java.lang.Thread.run    Thread.java, line 841  

2

1   java.lang.Object.wait   
2   java.lang.Object.wait   Object.java, line 401
3   java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 102
4   java.lang.ref.ReferenceQueue.remove ReferenceQueue.java, line 73
5   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
6   java.lang.Thread.run

3

1   java.util.HashMap.newKeyIterator    HashMap.java, line 907
2   java.util.HashMap$KeySet.iterator   HashMap.java, line 913
3   java.util.HashSet.iterator  HashSet.java, line 161
4   java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers    ThreadPoolExecutor.java, line 755
5   java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers    ThreadPoolExecutor.java, line 778
6   java.util.concurrent.ThreadPoolExecutor.shutdown    ThreadPoolExecutor.java, line 1357
7   java.util.concurrent.ThreadPoolExecutor.finalize    ThreadPoolExecutor.java, line 1443
8   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
9   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
10  java.lang.Thread.run

4

1   com.android.internal.os.BinderInternal$GcWatcher.finalize   BinderInternal.java, line 47
2   java.lang.Daemons$FinalizerDaemon.doFinalize    Daemons.java, line 187
3   java.lang.Daemons$FinalizerDaemon.run   Daemons.java, line 170
4   java.lang.Thread.run

2
ไม่เป็นไรพบว่ามันbugzilla.mozilla.org/show_bug.cgi?id=864102ฉันสามารถยืนยันได้ว่ามีผลกระทบต่อแอพของเรามันมีกลิ่นเหมือนปัญหาของ Google Play Services
eveliotc

บรรทัดของรหัสที่โยนข้อผิดพลาดถูกนำมาใช้เวอร์ชัน 4.3_r1 ซึ่งวางจำหน่ายในวันที่ 5 มิถุนายน 2013 อาจเป็นปัญหาที่เกิดขึ้นตั้งแต่นั้นมา
edubriguenti

Android เวอร์ชัน 4.2.2 ก็เริ่มที่จะโยนข้อยกเว้นนี้ดังนั้นอาจจะเป็นการอัพเดทของ Google Play ที่เป็นที่มา
JWqvist

@EvelioTarazona ฉันมีในแอพบางตัวที่ไม่ได้ใช้ play-services
ligi

@ligi มันเป็น stack-trace แบบเดียวกับคุณหรือเปล่า?
eveliotc

คำตอบ:


220

การเปิดเผยแบบเต็ม - ฉันเป็นผู้เขียนการพูดคุยที่กล่าวถึงก่อนหน้านี้ใน TLV DroidCon

ฉันมีโอกาสตรวจสอบปัญหานี้ในแอปพลิเคชั่น Android จำนวนมากและพูดคุยกับนักพัฒนาคนอื่น ๆ ที่พบมัน - และเราทุกคนก็ถึงจุดเดียวกัน: ปัญหานี้ไม่สามารถหลีกเลี่ยงได้

ฉันลองดูที่การใช้งานเริ่มต้นของรหัสตัวเก็บรวบรวมขยะ Android อย่างละเอียดยิ่งขึ้นเพื่อให้เข้าใจได้ดีขึ้นว่าทำไมข้อยกเว้นนี้จึงเกิดขึ้นและสิ่งที่อาจเป็นสาเหตุที่เป็นไปได้ ฉันยังพบสาเหตุที่เป็นไปได้ระหว่างการทดลอง

รากของปัญหาอยู่ที่จุดที่อุปกรณ์ "เข้าสู่โหมดสลีป" ชั่วขณะหนึ่งซึ่งหมายความว่าระบบปฏิบัติการได้ตัดสินใจที่จะลดการใช้แบตเตอรี่โดยการหยุดกระบวนการผู้ใช้ส่วนใหญ่เป็นระยะเวลาหนึ่งและปิดหน้าจอลดรอบ CPU เป็นต้นวิธีนี้จะทำ - อยู่ในระดับระบบ Linux ที่กระบวนการหยุดกลางคัน สิ่งนี้สามารถเกิดขึ้นได้ตลอดเวลาในระหว่างการเรียกใช้แอปพลิเคชันปกติ แต่จะหยุดที่การเรียกระบบเนทีฟเนื่องจากการสลับบริบทเสร็จสิ้นในระดับเคอร์เนล ดังนั้น - นี่คือที่ Dalvik GC เข้าร่วมเรื่องราว

รหัส Dalvik GC (ตามที่ดำเนินการในโครงการ Dalvik ในเว็บไซต์ AOSP) ไม่ใช่รหัสที่ซับซ้อน วิธีพื้นฐานในการทำงานนั้นครอบคลุมอยู่ในสไลด์ DroidCon ของฉัน สิ่งที่ฉันไม่ได้ครอบคลุมคือการวนรอบ GC ขั้นพื้นฐาน - ณ จุดที่นักสะสมมีรายการของวัตถุที่จะจบ (และทำลาย) ตรรกะวงที่ฐานสามารถลดความซับซ้อนเช่นนี้:

  1. ใช้เวลาstarting_timestamp,
  2. ลบวัตถุสำหรับรายการของวัตถุที่จะปล่อย
  3. ปล่อยวัตถุ - finalize()และเรียกdestroy()ใช้เนทิฟถ้าจำเป็น
  4. ใช้เวลาend_timestamp,
  5. คำนวณ ( end_timestamp - starting_timestamp) และเปรียบเทียบกับค่าไทม์เอาต์ของรหัสฮาร์ด 10 วินาที
  6. หากหมดเวลาใช้งานแล้วให้โยนjava.util.concurrent.TimeoutExceptionและฆ่ากระบวนการ

พิจารณาสถานการณ์สมมติต่อไปนี้:

แอพลิเคชันทำงานพร้อมทำสิ่งนั้น

นี่ไม่ใช่แอปพลิเคชันที่ผู้ใช้หันหน้าไปทาง แต่จะทำงานในพื้นหลัง

ในระหว่างการดำเนินการพื้นหลังนี้วัตถุถูกสร้างใช้และจำเป็นต้องรวบรวมเพื่อปล่อยหน่วยความจำ

แอปพลิเคชั่นไม่รบกวนการทำงานของ WakeLock เพราะจะส่งผลต่อแบตเตอรี่และดูเหมือนไม่จำเป็น

ซึ่งหมายความว่าแอปพลิเคชันจะเรียกใช้ GC เป็นครั้งคราว

โดยปกติการรัน GC จะเสร็จสมบูรณ์โดยไม่มีการผูกปม

บางครั้ง (ไม่ค่อยมาก) ระบบจะตัดสินใจนอนในระหว่างการทำงานของ GC

สิ่งนี้จะเกิดขึ้นหากคุณเรียกใช้แอปพลิเคชันของคุณนานพอและตรวจสอบบันทึกของหน่วยความจำ Dalvik อย่างใกล้ชิด

ตอนนี้ให้พิจารณาตรรกะการประทับเวลาของการวนรอบ GC พื้นฐาน - เป็นไปได้ที่อุปกรณ์จะเริ่มการทำงานใช้ a start_stampและเข้าสู่โหมดสลีปที่การdestroy()เรียกใช้ดั้งเดิมบนวัตถุระบบ

เมื่อมันกลับมาทำงานต่อและดำเนินการต่อdestroy()จะเสร็จสิ้นและต่อไปend_stampจะเป็นเวลาที่ใช้เวลาdestroy()โทร + เวลานอน

หากเวลานอนนาน (มากกว่า 10 วินาที) java.util.concurrent.TimeoutExceptionจะถูกโยนทิ้ง

ฉันได้เห็นสิ่งนี้ในกราฟที่สร้างขึ้นจากการวิเคราะห์สคริปต์ไพ ธ อน - สำหรับแอปพลิเคชันระบบ Android ไม่ใช่เฉพาะแอพที่ถูกตรวจสอบเท่านั้น

รวบรวมบันทึกเพียงพอและในที่สุดคุณจะเห็นมัน

บรรทัดล่างสุด:

ไม่สามารถหลีกเลี่ยงปัญหานี้ได้ - คุณจะพบปัญหาหากแอปของคุณทำงานในพื้นหลัง

คุณสามารถบรรเทาได้ด้วยการใช้ WakeLock และป้องกันไม่ให้อุปกรณ์เข้าสู่โหมดสลีป แต่นั่นเป็นเรื่องราวที่แตกต่างกันโดยสิ้นเชิงและปวดหัวใหม่

คุณสามารถลดปัญหาได้โดยลดการเรียก GC - ทำให้สถานการณ์มีโอกาสน้อยลง (เคล็ดลับอยู่ในสไลด์)

ฉันยังไม่ได้มีโอกาสได้รับรหัส GC Dalvik 2 (aka ART) - ซึ่งมีคุณสมบัติ Generational Compacting ใหม่หรือทำการทดลองบน Android Lollipop

เพิ่ม 7/5/2015:

หลังจากตรวจสอบข้อผิดพลาดรายงานการรวมสำหรับประเภทความผิดพลาดนี้ดูเหมือนว่าข้อผิดพลาดเหล่านี้จากรุ่น Android 5.0 ขึ้นไปของระบบปฏิบัติการ Android (Lollipop with ART) บัญชีเพียง 0.5% ของประเภทความผิดพลาดนี้ ซึ่งหมายความว่าการเปลี่ยนแปลง ART GC ได้ลดความถี่ของการขัดข้องเหล่านี้

เพิ่ม 6/1/2559:

ดูเหมือนว่าโครงการ Android ได้เพิ่มข้อมูลจำนวนมากเกี่ยวกับวิธีการทำงานของ GC ใน Dalvik 2.0 (aka ART)

คุณสามารถอ่านได้ที่นี่ - Debugging เก็บขยะ

นอกจากนี้ยังกล่าวถึงเครื่องมือบางอย่างเพื่อรับข้อมูลเกี่ยวกับพฤติกรรมของ GC สำหรับแอปของคุณ

การส่ง SIGQUIT ไปยังกระบวนการแอปของคุณจะทำให้ ANR เป็นหลักและดัมพ์สถานะแอปพลิเคชันไปยังล็อกไฟล์สำหรับการวิเคราะห์


ในกรณีของฉันฉันยังวางแผนที่จะพยายามลดสิ่งนี้โดยการหาวิธีลดจำนวนรหัส / เวลาที่ฉันทำงานในพื้นหลัง ขอบคุณสำหรับการวิจัยในหัวข้อ
parkerfath

การลบการประมวลผลเบื้องหลังในแอปของคุณจะช่วยลดปัญหาได้อย่างมาก
oba

สำหรับสิ่งที่มีค่าสิ่งนี้จะเกิดขึ้นใน Marshmallow (6.0.1) ที่กล่าวว่าฉันเคยได้รับข้อผิดพลาดนี้เพียงครั้งเดียว ดังนั้นดูเหมือนจะไม่เป็นปัญหาใหญ่โต ขอบคุณสำหรับคำอธิบายอย่างละเอียด
Knossos

หลังจากผ่านไประยะหนึ่งฉันได้รับความประทับใจที่ชัดเจนว่าการแก้ไขปัญหานี้ในระบบปฏิบัติการเป็นปัญหาอย่างมากและต้องการความร่วมมือระหว่าง Google และ OEM ฉันไม่คาดหวังว่าสิ่งนี้จะได้รับการแก้ไขตลอดเวลาในไม่ช้า
โอบา

ฉันใช้ wakelock แต่ยังคงพบปัญหานี้ใน Android 4.4.2 แอพของฉันมีการทำงานบางอย่างในพื้นหลัง แต่ส่วนใหญ่ออกแบบมาเพื่อให้ทำงานได้ตลอดวันในขณะที่ติดตั้งสายชาร์จ มีวิธีอื่นในการบรรเทาปัญหานี้หรือไม่?
Orcun Sevsay

74

เราเห็นสิ่งนี้อย่างต่อเนื่องตลอดทั้งแอปของเราโดยใช้ Crashlytics ความผิดพลาดมักเกิดขึ้นในโค้ดแพลตฟอร์ม ตัวอย่างเล็ก ๆ :

android.database.CursorWindow.finalize () หมดเวลาหลังจาก 10 วินาที

java.util.regex.Matcher.finalize () หมดเวลาหลังจาก 10 วินาที

android.graphics.Bitmap $ BitmapFinalizer.finalize () หมดเวลาหลังจาก 10 วินาที

org.apache.http.impl.conn.SingleClientConnManager.finalize () หมดเวลาหลังจาก 10 วินาที

java.util.concurrent.ThreadPoolExecutor.finalize () หมดเวลาหลังจาก 10 วินาที

android.os.BinderProxy.finalize () หมดเวลาหลังจาก 10 วินาที

android.graphics.Path.finalize () หมดเวลาหลังจาก 10 วินาที

อุปกรณ์ที่เกิดเหตุการณ์นี้เป็นอุปกรณ์ที่ผลิตโดยซัมซุง (แต่ไม่เฉพาะ) นั่นอาจหมายความว่าผู้ใช้ส่วนใหญ่ของเราใช้อุปกรณ์ Samsung; อีกวิธีหนึ่งก็อาจบ่งบอกถึงปัญหากับอุปกรณ์ซัมซุง ฉันไม่แน่ใจจริงๆ

ฉันคิดว่านี่ไม่ได้ตอบคำถามของคุณจริง ๆ แต่ฉันแค่อยากจะเสริมว่านี่เป็นเรื่องธรรมดาและไม่ได้เฉพาะเจาะจงกับแอปพลิเคชันของคุณ


16
มันเกิดขึ้นสำหรับรุ่น Android 5.0.1 เช่นกันและดูเหมือนจะไม่ถูก จำกัด ให้ใช้กับอุปกรณ์ Samsung มันเกิดขึ้นใน Nexus 6
Shobhit Puri

4
ฉันมีปัญหานี้กับ android 4.4.4 พร้อมอุปกรณ์ที่ผลิตโดย XIAOMI
Paresh Dudhat

แค่อยากจะพูดว่าเรากำลังเห็นปัญหาส่วนใหญ่บนแท็บเล็ตซัมซุง ไม่แน่ใจว่าซัมซุงทำอะไรที่แตกต่างกับวิธีที่แท็บเล็ตจัดการแอปที่มีพื้นหลัง
FriendlyMikhail

1
ฉันมีปัญหานี้ใน Android 4.4.4 อุปกรณ์ที่ผลิตโดย HUAWEI
Rameshbabu

1
แอพของฉันล่มหลังจากนั้นถ้าฉันใช้ไลบรารีนกขมิ้นรั่วบนอุปกรณ์ Android 5.0.2 ของซัมซุง หากฉันปิดใช้งานการเริ่มต้นของไลบรารีแอพก็ใช้ได้ดี
vanomart

15

ฉันพบสไลด์บางอย่างเกี่ยวกับปัญหานี้

http://de.slideshare.net/DroidConTLV/android-crash-analysis-and-the-dalvik-garbage-collector-tools-and-tips

ในสไลด์นี้ผู้เขียนบอกว่าดูเหมือนว่าจะมีปัญหากับ GC หากมีวัตถุจำนวนมากหรือวัตถุขนาดใหญ่ในกอง สไลด์ยังรวมถึงการอ้างอิงไปยังแอปตัวอย่างและสคริปต์หลามเพื่อวิเคราะห์ปัญหานี้

https://github.com/oba2cat3/GCTest

https://github.com/oba2cat3/logcat2memorygraph

นอกจากนี้ฉันพบคำใบ้ในความคิดเห็น # 3 ที่ด้านนี้: https://code.google.com/p/android/issues/detail?id=53418#c3


7

FinalizerWatchdogDaemonเราแก้ปัญหาโดยการหยุด

public static void fix() {
    try {
        Class clazz = Class.forName("java.lang.Daemons$FinalizerWatchdogDaemon");

        Method method = clazz.getSuperclass().getDeclaredMethod("stop");
        method.setAccessible(true);

        Field field = clazz.getDeclaredField("INSTANCE");
        field.setAccessible(true);

        method.invoke(field.get(null));

    }
    catch (Throwable e) {
        e.printStackTrace();
    }
}

attachBaseContext()คุณสามารถโทรหาวิธีการในวงจรชีวิตของโปรแกรมประยุกต์เช่น ด้วยเหตุผลเดียวกันคุณสามารถระบุการผลิตของโทรศัพท์เพื่อแก้ไขปัญหาได้ขึ้นอยู่กับคุณ


ใช้งานไม่ได้สำหรับเราฉันไม่เข้าใจว่าทำไม รหัสนั้นเสร็จสมบูรณ์โดยไม่มีข้อยกเว้น แต่เรายังคงได้รับปัญหาเหล่านั้นในรายงาน Crashlytics และ Google Play Console
Anton Breusov

5

Broadcast Receivers หมดเวลาหลังจาก 10 วินาที อาจเป็นไปได้ว่าคุณกำลังทำการโทรแบบอะซิงโครนัส (ผิด) จากเครื่องรับสัญญาณออกอากาศและ 4.3 ตรวจพบว่าเป็นจริง


3
ดูเหมือนจะไร้ประโยชน์ในการตรวจจับและไม่ได้บอกคุณมากพอ แจ้งให้เราทราบว่าการออกอากาศใดจะดี
Aaron T Harris

ขออภัยถ้าฉันผิด แต่ฉันไม่คิดว่าการหมดเวลาของตัวรับสัญญาณออกอากาศจะทำให้เกิดความผิดพลาดนี้ เป็นวิธีปฏิบัติที่ดีในการหลีกเลี่ยงการ จำกัด 10s แต่นั่นเป็นปัญหาที่แตกต่างจากผู้ร้องขอ
parkerfath

ฉันมีเวลาแค่ 10 วินาทีในสมอง developer.android.com/training/articles/perf-anr.html IDK หากเป็นสาเหตุของความผิดพลาด
danny117

ประเด็นของคุณแข็งแกร่งและเป็นแนวปฏิบัติที่ดี อย่างไรก็ตามโปสเตอร์ต้นฉบับมีคำถามเฉพาะเกี่ยวกับชุดอุปกรณ์เฉพาะ ฉันขอแนะนำให้ผู้ชมคนอื่น ๆ ของโพสต์นี้เพื่อตรวจสอบคำตอบของคริสโตเฟอร์และคำตอบของโอบาถ้าพวกเขาเห็นอาการเดียวกัน (อุปกรณ์ Samsung (esp. Galaxy S 4), ฯลฯ )
parkerfath

ฉันไม่ได้อยู่ที่นี่เพื่อทุบตีอุปกรณ์ผลิตมันจะผิดเงื่อนไข
danny117

5

นี่คือวิธีแก้ปัญหาที่มีประสิทธิภาพจาก didi เพื่อแก้ปัญหานี้เนื่องจากข้อผิดพลาดนี้พบได้บ่อยและหาสาเหตุได้ยากดูเหมือนว่าปัญหาของระบบทำไมเราไม่สามารถเพิกเฉยได้โดยตรง? แน่นอนเราไม่สนใจเลยที่นี่ คือรหัสตัวอย่าง:

final Thread.UncaughtExceptionHandler defaultUncaughtExceptionHandler = 
        Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        if (t.getName().equals("FinalizerWatchdogDaemon") && e instanceof TimeoutException) {
        } else {
            defaultUncaughtExceptionHandler.uncaughtException(t, e);
        }
    }
});

โดยการตั้งค่าตัวจัดการข้อยกเว้นที่ไม่ได้ตรวจสอบเป็นค่าเริ่มต้นพิเศษแอปพลิเคชันสามารถเปลี่ยนวิธีการจัดการข้อยกเว้นที่ไม่ถูกตรวจจับสำหรับเธรดเหล่านั้นซึ่งจะยอมรับพฤติกรรมเริ่มต้นใด ๆ ก็ตามที่ระบบจัดเตรียมไว้ เมื่อไม่TimeoutExceptionถูกจับถูกโยนออกจากเธรดชื่อFinalizerWatchdogDaemonตัวจัดการพิเศษนี้จะบล็อกเชนตัวจัดการระบบจะไม่ถูกเรียกใช้ตัวจัดการระบบดังนั้นจะหลีกเลี่ยงการขัดข้อง

จากการฝึกฝนไม่พบผลเสียอื่น ๆ ระบบ GC ยังคงทำงานอยู่การหมดเวลาจะลดลงเมื่อการใช้งาน CPU ลดลง

ดูรายละเอียดเพิ่มเติมได้ที่: https://mp.weixin.qq.com/s/uFcFYO2GtWWiblotem2bGg


4

สิ่งหนึ่งที่เป็นความจริงอย่างคงเส้นคงวาคือในเวลานี้อุปกรณ์จะหายใจไม่ออกสำหรับหน่วยความจำบางส่วน

ก่อนหน้านี้ผู้เขียนเกือบทั้งหมดกล่าวถึงปัญหานี้เมื่อ Android พยายามเรียกใช้ GC ในขณะที่แอปอยู่ในพื้นหลัง ในกรณีส่วนใหญ่ที่เราสังเกตเห็นผู้ใช้หยุดแอปชั่วคราวโดยล็อคหน้าจอ นี่อาจหมายถึงหน่วยความจำรั่วที่ใดที่หนึ่งในแอปพลิเคชันหรืออุปกรณ์ที่โหลดเกินไปแล้ว ดังนั้นวิธีที่ถูกกฎหมายเพียงวิธีเดียวที่จะย่อให้เล็กคือ:

  • เพื่อให้แน่ใจว่าไม่มีการรั่วไหลของหน่วยความจำและ
  • เพื่อลดขนาดหน่วยความจำของแอปโดยทั่วไป

1
try {
    Class<?> c = Class.forName("java.lang.Daemons");
    Field maxField = c.getDeclaredField("MAX_FINALIZE_NANOS");
    maxField.setAccessible(true);
    maxField.set(null, Long.MAX_VALUE);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (NoSuchFieldException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
}

สิ่งนี้จะไม่แก้ปัญหาในกรณีที่ระยะเวลาสลีป> 100 วินาที ทำไมไม่ตั้งเป็น MAX_INT
oba

ใช่ฉันแค่ทำตัวอย่าง ~
kot32

1
สิ่งนี้ไม่สามารถใช้งานได้เนื่องจากการอินไลน์คงที่ การเปลี่ยนค่าฟิลด์จะไม่ส่งผลกระทบต่อค่า inlined to callers
hqzxzwb

0

finalizeQueue อาจยาวเกินไป

ฉันคิดว่า java อาจต้องใช้GC.SuppressFinalize () & GC.ReRegisterForFinalize ()เพื่อให้ผู้ใช้ลดความยาวขั้นสุดท้ายที่สรุปได้อย่างชัดเจน

หากมีซอร์สโค้ดของ JVM อยู่อาจนำวิธีการเหล่านี้ไปใช้กับเราเช่น android ROM maker


0

ดูเหมือนข้อบกพร่อง Android Runtime ดูเหมือนจะมี finalizer ที่ทำงานในเธรดแยกต่างหากและการเรียก finalize () วิธีการในวัตถุหากพวกเขาไม่ได้อยู่ในกรอบปัจจุบันของสแต็ค ตัวอย่างเช่นรหัสต่อไปนี้ (สร้างขึ้นเพื่อตรวจสอบปัญหานี้) จบลงด้วยความผิดพลาด

มีเคอร์เซอร์ที่ทำอะไรบางอย่างในขั้นตอนสุดท้าย (เช่น SqlCipher อัน, ให้ปิด () ซึ่งล็อคไปยังฐานข้อมูลที่ใช้อยู่ในปัจจุบัน)

private static class MyCur extends MatrixCursor {


    public MyCur(String[] columnNames) {
        super(columnNames);
    }

    @Override
    protected void finalize() {
        super.finalize();

        try {
            for (int i = 0; i < 1000; i++)
                Thread.sleep(30);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

และเราทำสิ่งที่ใช้เวลานานโดยมีเคอร์เซอร์ที่เปิดอยู่:

for (int i = 0; i < 7; i++) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                MyCur cur = null;
                try {
                    cur = new MyCur(new String[]{});
                    longRun();
                } finally {
                    cur.close();
                }
            }

            private void longRun() {
                try {
                    for (int i = 0; i < 1000; i++)
                        Thread.sleep(30);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

ซึ่งทำให้เกิดข้อผิดพลาดดังต่อไปนี้:

FATAL EXCEPTION: FinalizerWatchdogDaemon
                                                                        Process: la.la.land, PID: 29206
                                                                        java.util.concurrent.TimeoutException: MyCur.finalize() timed out after 10 seconds
                                                                            at java.lang.Thread.sleep(Native Method)
                                                                            at java.lang.Thread.sleep(Thread.java:371)
                                                                            at java.lang.Thread.sleep(Thread.java:313)
                                                                            at MyCur.finalize(MessageList.java:1791)
                                                                            at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
                                                                            at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
                                                                            at java.lang.Thread.run(Thread.java:762)

ชุดการผลิตที่มี SqlCipher นั้นคล้ายคลึงกันมาก:

12-21 15:40:31.668: E/EH(32131): android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): java.util.concurrent.TimeoutException: android.content.ContentResolver$CursorWrapperInner.finalize() timed out after 10 seconds
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Object.wait(Native Method)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Thread.parkFor$(Thread.java:2128)
12-21 15:40:31.668: E/EH(32131): 	at sun.misc.Unsafe.park(Unsafe.java:325)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.LockSupport.park(LockSupport.java:161)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:840)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:873)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1197)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:200)
12-21 15:40:31.668: E/EH(32131): 	at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteDatabase.lock(SourceFile:518)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteProgram.close(SourceFile:294)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteQuery.close(SourceFile:136)
12-21 15:40:31.668: E/EH(32131): 	at net.sqlcipher.database.SQLiteCursor.close(SourceFile:510)
12-21 15:40:31.668: E/EH(32131): 	at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): 	at android.database.CursorWrapper.close(CursorWrapper.java:50)
12-21 15:40:31.668: E/EH(32131): 	at android.content.ContentResolver$CursorWrapperInner.close(ContentResolver.java:2746)
12-21 15:40:31.668: E/EH(32131): 	at android.content.ContentResolver$CursorWrapperInner.finalize(ContentResolver.java:2757)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:222)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Daemons$FinalizerDaemon.run(Daemons.java:209)
12-21 15:40:31.668: E/EH(32131): 	at java.lang.Thread.run(Thread.java:762)

ดำเนินการต่อ: ปิดเคอร์เซอร์โดยเร็ว อย่างน้อยใน Samsung S8 ที่ใช้ Android 7 ที่มีปัญหาได้รับการเห็น


0

สำหรับคลาสที่คุณสร้าง (เช่นไม่ได้เป็นส่วนหนึ่งของ Android) เป็นไปได้ที่จะหลีกเลี่ยงความผิดพลาดทั้งหมด

คลาสที่ใช้งานfinalize()มีความน่าจะเป็นที่หลีกเลี่ยงไม่ได้บางส่วนของการกระแทกตามที่อธิบายโดย @oba ดังนั้นแทนที่จะใช้ finalizers ที่จะดำเนินการล้าง, PhantomReferenceQueueการใช้งาน

สำหรับตัวอย่างตรวจสอบการใช้งานใน React Native: https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/jni/DestructorThread.java

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.