กระบวนการของแอปใน Android ไม่ได้ทำงานแยกกัน แอปพลิเคชันมักต้องอาศัย บริการที่แอปพลิเคชันอื่นหรือระบบเองเป็นผู้ให้บริการ เมื่อกระบวนการหนึ่งเชื่อมต่อกับอีกกระบวนการหนึ่งผ่านการผูกบริการ ระบบจะสร้างทรัพยากร Dependency ที่มีผลอย่างมากต่อวิธีที่เฟรมเวิร์ก Android จัดการหน่วยความจำ
สถานะกระบวนการและคะแนน OOM
เฟรมเวิร์ก Android ใช้สถานะของกระบวนการเพื่อติดตามความสำคัญของแต่ละกระบวนการที่ทำงาน
จากนั้น OomAdjuster จะใช้สถานะเหล่านี้เพื่อกำหนดค่าการปรับคะแนน OOM (oom_score_adj) ซึ่งมีค่าตั้งแต่ -1000 ถึง 1000
oom_score_adj ที่ต่ำกว่าหมายความว่ากระบวนการมีความสำคัญมากขึ้นและมีโอกาสน้อยที่จะ
ถูกหยุดทำงานเนื่องจากหน่วยความจำไม่เพียงพอ (LMK)
สถานะกระบวนการที่พบบ่อย
ตารางต่อไปนี้แสดงสถานะกระบวนการที่พบบ่อยที่สุดบางส่วนและค่า oom_score_adj โดยทั่วไป ดูรายการที่สมบูรณ์และเป็นปัจจุบันได้ที่
android.app.ActivityManager และ com.android.server.am.psc.Constants ใน
ซอร์สโค้ดของ Android
| สถานะกระบวนการ (ย่อ) | คำอธิบาย | oom_score_adjปกติ |
|---|---|---|
| PER (ถาวร) | กระบวนการของระบบที่ต้องทำงานอยู่เสมอ (เช่น โทรศัพท์) | -800 |
| TOP | กระบวนการที่ผู้ใช้โต้ตอบด้วยในปัจจุบัน | 0 |
| VIS (มองเห็นได้) | กระบวนการมีกิจกรรมที่มองเห็นได้ (เช่น อยู่เบื้องหลังกล่องโต้ตอบโปร่งแสง) | 100 |
| PERC (รับรู้ได้) | กระบวนการเบื้องหลังที่ผู้ใช้ทราบ (เช่น การเล่นเพลง) | 200 |
| FGS | กระบวนการโฮสต์บริการที่ทำงานอยู่เบื้องหน้า | 0 ถึง 200 (แตกต่างกันไป) |
| BTOP (Bound Top) | กระบวนการที่ผูกกับใบสมัคร TOP | 100 |
| BFGS | บริการที่ทำงานอยู่เบื้องหน้าที่เชื่อมโยง (โดยปกติจะเชื่อมโยงกับระบบ) | 0 |
| PREV (ก่อนหน้า) | กระบวนการสุดท้ายที่ผู้ใช้ดำเนินการก่อนกระบวนการปัจจุบัน | 700 |
| แคช | แอปที่ทำงานเบื้องหลังซึ่งปิดได้อย่างปลอดภัย | 900 ถึง 999 |
ผลกระทบของการเชื่อมโยงบริการ
เมื่อกระบวนการไคลเอ็นต์ (เช่น แอปในสถานะ TOP) เชื่อมโยงกับบริการในกระบวนการเซิร์ฟเวอร์ กระบวนการเซิร์ฟเวอร์มักจะรับช่วงลำดับความสำคัญที่สูงขึ้น ซึ่งจะช่วยให้บริการยังคงพร้อมใช้งานตราบใดที่ไคลเอ็นต์ต้องการ

การควบคุมการรับค่าด้วยแฟล็ก BIND
การรับค่าเป็นลักษณะการทำงานเริ่มต้นเมื่อใช้ Context.BIND_AUTO_CREATE
อย่างไรก็ตาม นักพัฒนาแอปสามารถควบคุมวิธีที่การเชื่อมโยงส่งผลต่อความสำคัญของกระบวนการเป้าหมายได้
โดยใช้ Flag ต่างๆ ใน bindService()
แฟล็ก BIND ที่สำคัญสำหรับคะแนน OOM
การแจ้งต่อไปนี้มีความเกี่ยวข้องมากที่สุดเมื่อจัดการแรงดันหน่วยความจำทั่วทั้งระบบ
BIND_AUTO_CREATE: การแจ้งว่าไม่เหมาะสมที่พบบ่อยที่สุด ซึ่งจะช่วยให้กระบวนการบริการ เริ่มต้นและทำงานต่อไปได้ตราบใดที่มีการเชื่อมโยงอยู่ โดยค่าเริ่มต้น ระบบจะ เพิ่มลำดับความสำคัญของกระบวนการเซิร์ฟเวอร์ให้ตรงกับไคลเอ็นต์ด้วยBIND_NOT_FOREGROUND: ป้องกันไม่ให้กระบวนการของบริการเป้าหมายได้รับการเลื่อนขึ้นไปเป็นลำดับความสำคัญการจัดกำหนดการที่ทำงานอยู่เบื้องหน้า (ลำดับความสำคัญของ CPU) แต่ยังคงอนุญาตให้เพิ่มลำดับความสำคัญของหน่วยความจำ (oom_score_adj) ได้ ซึ่งจะเป็นประโยชน์สำหรับงานที่ทำงานเบื้องหลังซึ่งไม่ควรแข่งขันกับ UI สำหรับรอบ CPU แต่ยังคงควรได้รับการป้องกันไม่ให้ถูกปิดBIND_WAIVE_PRIORITY: แฟล็กที่เข้มงวดมากซึ่งสั่งให้ระบบไม่ส่งผลต่อการจัดกำหนดการหรือลำดับความสำคัญในการจัดการหน่วยความจำของกระบวนการเป้าหมาย ระบบจะจัดการกระบวนการบริการราวกับว่าเป็นกระบวนการพื้นหลังปกติในรายการ LRU ซึ่งทำให้มีสิทธิ์ถูก OOM Kill แม้ว่าจะมีการเชื่อมโยงอยู่ก็ตามBIND_ABOVE_CLIENT: แสดงว่าบริการมีความสำคัญมากกว่าแอปไคลเอ็นต์เอง เมื่อระบบต้องการเรียกคืนหน่วยความจำ ระบบจะเลือกปิดแอปไคลเอ็นต์ก่อนปิดบริการที่มีผลผูกพัน ซึ่ง "แข็งแกร่งกว่า"BIND_AUTO_CREATEเนื่องจากมีการป้องกันอีกชั้นหนึ่งสำหรับ บริการโดยที่ไคลเอ็นต์ต้องรับภาระBIND_NOT_PERCEPTIBLE: ลดความสำคัญของบริการเป้าหมายให้อยู่ต่ำกว่าระดับPERCEPTIBLEเพื่อให้ระบบเรียกคืนหน่วยความจำเพื่อเพิ่มพื้นที่ สำหรับกระบวนการที่สำคัญกว่าซึ่งผู้ใช้รับรู้ได้
ภาคปฏิบัติ: สังเกตผลลัพธ์ของการเชื่อมโยง
เราจะใช้แอปพลิเคชัน MemoryLab เพื่อแสดงให้เห็นว่าการเชื่อมโยงจากแอปTOP
ส่งผลต่อสถานะของกระบวนการแยกต่างหากอย่างไร
1. เปิดตัว MemoryLab
คำสั่งต่อไปนี้จะเปิดแอป หลังจากเปิดแล้ว ตรวจสอบว่าแอปยังคง อยู่ในเบื้องหน้า (อย่ากดปุ่มหน้าแรกหรือเปลี่ยนแอปในตอนนี้)
adb shell am start -n com.android.memorylab/.MainActivity
2. ระบุกระบวนการ
ตรวจสอบสถานะกระบวนการก่อนเชื่อมโยง MemoryLab เรียกใช้ UI หลักในกระบวนการเดียวและมี RemoteService ที่ทำงานในกระบวนการ :remote
adb shell dumpsys activity processes com.android.memorylab
ตัวอย่างข้อมูลโค้ดเอาต์พุต
Process OOM control (154 total, non-act at 7, non-svc at 7):
Proc #0: fg T/A/TOP LCMNFUATI t: 0 13470:com.android.memorylab/u0a417 (top-activity)
oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0
state: cur=TOP set=TOP lastRss=0.00 lastCachedRss=0.00
คุณจะเห็นกระบวนการหลัก com.android.memorylab ในสถานะ TOP ยังไม่ได้เริ่มกระบวนการ
:remote
3. การเชื่อมโยงทริกเกอร์
ส่งประกาศไปยังแอปเพื่อทริกเกอร์การผูกบริการ
adb shell am broadcast -a com.android.memorylab.LEAK_BINDER
4. สังเกตสถานะที่สูงขึ้น
ตรวจสอบสถานะกระบวนการอีกครั้ง
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
ตัวอย่างข้อมูลโค้ดเอาต์พุต
Proc # 1: vis F/ /BTOP ---NFUATI t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100
state: cur=BTOP set=BTOP lastRss=0.00 lastCachedRss=0.00
ขณะนี้:remoteกระบวนการทำงานอยู่และอยู่ในสถานะ BTOP (Bound TOP) โดยมีoom_score_adj เป็น 100 ซึ่งได้รับการปกป้องมากกว่าบริการที่ทำงานอยู่เบื้องหลังทั่วไป (ซึ่งจะอยู่ที่ 500 ขึ้นไป) อย่างมาก สัญกรณ์
<=Proc{...} แสดงกระบวนการที่รับผิดชอบการเพิ่มลำดับความสำคัญนี้
5. ส่งไปยังเบื้องหลัง
กดปุ่มหน้าแรกบนอุปกรณ์ ตรวจสอบสถานะอีกครั้ง
adb shell dumpsys activity processes | grep -A 10 "com.android.memorylab"
ตัวอย่างข้อมูลโค้ดเอาต์พุต
Proc # 2: prev b/ /LAST --------I t: 0 13560:com.android.memorylab:remote/u0a417 (service)
com.android.memorylab/.RemoteService<=Proc{13470:com.android.memorylab/u0a417}
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=0.00 lastCachedRss=0.00
Proc # 1: prev b/ /LAST --------I t: 0 13470:com.android.memorylab/u0a417 (previous)
oom: max=1001 curRaw=700 setRaw=700 cur=700 set=700
state: cur=LAST set=LAST lastRss=209MB lastCachedRss=0.00
ตอนนี้ทั้ง 2 กระบวนการได้เปลี่ยนไปอยู่ในสถานะที่มีลำดับความสำคัญต่ำกว่า (PREV /
oom_score_adj 700) เนื่องจากกระบวนการไคลเอ็นต์ไม่ได้เป็น TOP อีกต่อไป (หมายเหตุ:
LAST ในการดัมพ์สถานะหมายถึงสถานะภายในของ LAST_ACTIVITY ซึ่ง
แมปกับ PREV ในข้อมูลสรุประดับสูง)
การวิเคราะห์ด้วย procstats
procstats จะแสดงมุมมองย้อนหลังของสถานะเหล่านี้
# View stats for MemoryLab over the last hour
adb shell dumpsys procstats --hours 1 com.android.memorylab
ตัวอย่างข้อมูลโค้ดเอาต์พุต
* com.android.memorylab / u0a417 / v37:
* Prc com.android.memorylab / u0a417 / v37:
TOTAL: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
Top: 0.89% (0.00-0.00-0.00/0.00-0.00-0.00/210MB-210MB-210MB over 1)
* Prc com.android.memorylab:remote / u0a417 / v37:
TOTAL: 0.19%
Bnd Top: 0.19%
ในที่นี้ Bnd Top แสดงเปอร์เซ็นต์ของเวลาที่กระบวนการระยะไกลใช้
ในการผูกกับแอปพลิเคชันในสถานะ TOP
การบันทึกและวิเคราะห์การเชื่อมโยงด้วย Perfetto
แม้ว่า dumpsys จะให้ภาพรวม แต่ Perfetto ช่วยให้คุณเห็นช่วงเวลาที่เกิดการเชื่อมโยงและวิธีที่คะแนน OOM เปลี่ยนแปลงแบบเรียลไทม์ได้อย่างแม่นยำ
1. บันทึกการติดตาม
ใช้การกำหนดค่าที่มี linux.process_stats และหมวดหมู่ am atrace ดังนี้
adb shell perfetto -c - --txt -o /data/misc/perfetto-traces/service_bindings.perfetto-trace <<EOF
buffers: { size_kb: 65536 }
data_sources: {
config {
name: "linux.process_stats"
process_stats_config { proc_stats_poll_ms: 100 }
}
}
data_sources: {
config {
name: "linux.ftrace"
ftrace_config { ftrace_events: "am/am_proc_bound" }
}
}
duration_ms: 15000
EOF
2. การเปลี่ยนคะแนน OOM ของการค้นหา
เมื่อใช้ PerfettoSQL คุณจะเห็นว่าคะแนน OOM ของกระบวนการระยะไกลเปลี่ยนแปลงอย่างไร เมื่อเทียบกับกระบวนการ UI
SELECT ts, p.name, value AS oom_score_adj
FROM counter c
JOIN process_counter_track t ON c.track_id = t.id
JOIN process p USING (upid)
WHERE p.name LIKE 'com.android.memorylab%'
AND t.name = 'oom_score_adj'
ORDER BY ts;
3. ระบุเหตุการณ์การเชื่อมโยง
หากต้องการดูว่ามีการสร้างการอ้างอิงที่เชื่อมโยงเมื่อใดและกระบวนการใด เป็นผู้เริ่มต้น ให้ใช้การค้นหานี้
SELECT
s.ts,
p.name AS process_name,
t.name AS thread_name,
s.name AS slice_name
FROM slice s
JOIN thread_track tt ON s.track_id = tt.id
JOIN thread t USING (utid)
JOIN process p USING (upid)
WHERE s.name LIKE 'bindService:{com.android.memorylab%';
การเชื่อมโยงจากระบบไปยังแอป
ระบบ Android เองมักจะเชื่อมโยงกับบริการในแอปของบุคคลที่สามเพื่อมอบ ฟังก์ชันหลัก โดยปกติแล้ว เป้าหมายของการเชื่อมโยงเหล่านี้คือการลดเวลาในการตอบสนอง การทำให้กระบวนการทำงานและอยู่ในหน่วยความจำจะช่วยให้ระบบหลีกเลี่ยงค่าใช้จ่ายที่สูง ของ "Cold Start" (การโหลด APK, การเริ่มต้นรันไทม์ และการสร้าง ออบเจ็กต์ Application) เมื่อเกิดการโต้ตอบของผู้ใช้ที่สำคัญ นอกจากนี้ ยังมีการเชื่อมโยงอื่นๆ เพื่อป้องกันการเริ่มระบบใหม่บ่อยครั้งสำหรับแอปที่ต้องจัดการสตรีมของ เหตุการณ์ในเบื้องหลัง
ต่อไปนี้คือตัวอย่างในชีวิตจริงที่คุณอาจเห็นในอุปกรณ์ทั่วไป
VoiceInteractor
ผู้ใช้คาดหวังให้ระบบปฏิบัติการของโทรศัพท์มีผู้ช่วยดิจิทัลฝังอยู่ เพื่อให้สามารถ เรียกใช้ได้ทันทีด้วยคีย์เวิร์ดที่พูดหรือท่าทางสัมผัสที่รวดเร็ว และ การโต้ตอบเป็นไปอย่างราบรื่นและไร้รอยต่อ
เมื่อมีการทริกเกอร์ผู้ช่วย (เช่น คำสั่งเริ่มต้น "Ok Google" ในโทรศัพท์ Google
Pixel) ผู้ช่วยดิจิทัลจะต้องตอบกลับทันที เพื่อเป็นการยืนยันว่าsystem_serverจะผูกกับบริการโต้ตอบด้วยเสียงที่ผู้ใช้เลือกอย่างถาวร

หากตรวจสอบสถานะของกระบวนการ (เช่น ใช้ dumpsys activity processes) คุณอาจเห็นกระบวนการเช่น com.google.android.googlequicksearchbox:interactor ในสถานะ BFGS (บริการที่ทำงานอยู่เบื้องหน้าแบบผูก) ซึ่งยังคงทำงานอยู่ได้ด้วยการผูกจาก system_server (UID 1000)
NotificationListenerService
สำหรับการเชื่อมโยงระบบกับแอปบางอย่าง เป้าหมายไม่ใช่เวลาในการตอบสนอง แต่เป็นการป้องกัน
การเริ่มระบบแบบเย็นบ่อยๆ
NotificationListenerService
ซึ่งเป็นบริการที่รับสายจากระบบเมื่อมีการโพสต์หรือนำการแจ้งเตือนใหม่ออก
เป็นตัวอย่างที่ชัดเจน ผู้ใช้สมาร์ทโฟนทั่วไปอาจได้รับการแจ้งเตือนหลายร้อยรายการ
ตลอดทั้งวัน หากระบบยกเลิกการเชื่อมโยงจากเครื่องมือฟังการแจ้งเตือน
กระบวนการของแอปนั้นน่าจะเข้าสู่สถานะแคชและอาจถูก LMK ปิด
เมื่อมีการแจ้งเตือนครั้งถัดไป (อาจเกิดขึ้นในอีกไม่กี่วินาทีต่อมา) ระบบ จะบังคับให้เริ่มกระบวนการของแอปใหม่ทั้งหมดตั้งแต่ต้นเพียงเพื่อส่ง เหตุการณ์ วงจรการหยุดทำงานและการเริ่มทำงานใหม่ที่เกิดขึ้นอย่างต่อเนื่องนี้จะใช้ CPU และแบตเตอรี่มากกว่าการผูกและรักษากระบวนการให้ทำงานอยู่เบื้องหลัง
"หน้าจอ -1" (ฟีดข่าว) ของ Launcher
โดยปกติแล้ว แอป Launcher สมัยใหม่จะรวมฟังก์ชันการนำทางหลัก (ไอคอนและวิดเจ็ตหน้าแรก) เข้ากับฟีดข่าวที่พร้อมใช้งานบนหน้าจอ Launcher หน้าจอใดหน้าจอหนึ่ง และผสานรวมกับ UX ของ Launcher ได้อย่างราบรื่น ฟีดข่าวอาจมาจากแอปอื่น เช่น ใน Google Pixel นั้น Launcher จะผสานรวมกับฟีดที่มาจากแอป Google
เมื่อปัดไปทางซ้ายบนหน้าจอหลักเพื่อดูฟีดข่าว การเปลี่ยน ต้องราบรื่น Launcher ทำได้โดยการเชื่อมต่อกับอินเทอร์เฟซบริการใน แอปที่แสดงฟีดข่าว และรักษาการเชื่อมต่อดังกล่าวไว้ตราบใดที่ Launcher ยังทำงานอยู่ ซึ่งจะช่วยให้เนื้อหาในฟีดแสดงผลและพร้อมใช้งานใน หน่วยความจำแม้ว่าคุณจะไม่ได้ดูอยู่ก็ตาม
ตัวอย่างอื่นๆ ที่พบบ่อย
- Launcher (HOME_APP_ADJ): แอป Launcher (หน้าแรก) มีสล็อตพิเศษของตัวเอง
ในรายการลำดับความสำคัญ แม้ว่าไม่ได้ผูกกับบริการเสมอไป แต่จะมีการกำหนด
HOME_APP_ADJ(โดยปกติคือ 600) ระบบต้องการให้ตัวเรียกใช้ทำงานอยู่เสมอ เนื่องจากผู้ใช้กลับมาใช้ตัวเรียกใช้บ่อยครั้ง ในความเป็นจริงแล้ว ระบบจะเลือกปิดแอปที่ใช้ก่อนหน้านี้ (PREV_APP_ADJ = 700) มากกว่าปิด Launcher เนื่องจากหากปิด Launcher จะส่งผลให้ ประสบการณ์ของผู้ใช้ช้าลงเมื่อออกจากแอป เนื่องจากผู้ใช้จะต้องรอ ให้ Launcher เริ่มทำงานใหม่ - ตัวแก้ไขวิธีการป้อนข้อมูล (IME): เมื่อคุณพิมพ์ ระบบจะเชื่อมโยงกับแอปแป้นพิมพ์ที่คุณเลือก (เช่น Gboard) ซึ่งจะทำให้กระบวนการของแป้นพิมพ์อยู่ใน สถานะที่สูงขึ้นแม้ว่าจะซ่อนแป้นพิมพ์ชั่วคราวก็ตาม ซึ่งจะช่วยให้ แป้นพิมพ์ปรากฏขึ้นอีกครั้งได้ทันทีเมื่อคุณแตะช่องข้อความอื่น
- การชำระเงินผ่าน NFC: เมื่อคุณแตะโทรศัพท์เพื่อชำระเงิน ระบบจะเชื่อมโยงกับ บริการชำระเงินผ่าน NFC (เช่น Google Wallet) ธุรกรรมเหล่านี้มักมีข้อกำหนดแบบเรียลไทม์ที่เข้มงวดจากเทอร์มินัลของผู้ขาย หากแอปการชำระเงินต้อง Cold Start ธุรกรรมอาจหมดเวลาและล้มเหลว
ข้อดีข้อเสียและประสิทธิภาพที่ลดลง
แม้ว่าการเชื่อมโยงจะมีความจำเป็นต่อประสิทธิภาพและความถูกต้อง แต่ก็ส่งผลต่อประสิทธิภาพของหน่วยความจำของระบบ
- ความยืดหยุ่นลดลง: ทุกกระบวนการที่ผูกไว้คือกระบวนการที่ LMK ไม่ สามารถหยุดทำงานได้ง่ายๆ ซึ่งจะช่วยลด "กันชน" ของกระบวนการที่แคชไว้ซึ่งระบบ สามารถใช้เพื่อเพิ่มพื้นที่หน่วยความจำเมื่อมีการใช้งานหนัก
- การทำให้ประสิทธิภาพลดลงอย่างรวดเร็ว: หากมีการเชื่อมโยงกระบวนการมากเกินไป ระบบอาจพบว่ามีกระบวนการพื้นหลังที่สามารถหยุดได้น้อยมาก เมื่อ แรงกดดันด้านหน่วยความจำเพิ่มขึ้น ระบบจะ "ประสิทธิภาพลดลงอย่างรวดเร็ว" เร็วขึ้นมาก เนื่องจากระบบถูกบังคับให้ปิดกระบวนการที่สำคัญมากขึ้นหรือสลับ แคชหน้าเว็บ
รูปแบบที่ไม่ควรใช้ในการผูกบริการที่พบบ่อย
เนื่องจากการเชื่อมโยงบริการจะเพิ่มระดับ oom_score_adj โดยตรง ข้อผิดพลาดเล็กๆ น้อยๆ ในวงจร
เกี่ยวกับวิธีที่คุณได้มาหรือโครงสร้างการเชื่อมโยงอาจตรึงหน่วยความจำจำนวนมากในสถานะที่มีสิทธิ์ (BTOP, BFGS หรือ PERC) เป็นเวลาหลายชั่วโมง โปรดระวัง
รูปแบบต่อต้านที่พบบ่อยเหล่านี้เมื่อออกแบบหรือตรวจสอบบริการที่เชื่อมโยง
ลืม unbindService() ในไคลเอ็นต์ที่มีอายุการใช้งานยาวนาน
การเชื่อมโยงกับบริการจาก Application Singleton, Background Manager หรือ Activity ที่เรียก bindService() ใน onStart() โดยไม่มี unbindService() ที่ตรงกันใน onStop() จะทำให้ ServiceConnection รั่วไหล
ตราบใดที่การเชื่อมโยงนั้นยังคงใช้งานอยู่ กระบวนการเป้าหมายจะรับช่วงต่อ
ลำดับความสำคัญที่สูงขึ้นของไคลเอ็นต์ หากไคลเอ็นต์เป็นคอมโพเนนต์ของระบบที่ทำงานตลอดเวลาหรือเป็น
แอปที่ทำงานอยู่เบื้องหน้า กระบวนการของบริการที่เชื่อมโยงจะยังคงอยู่ใน BFGS หรือ BTOP
อย่างไม่มีกำหนด (แสดงใกล้ 100% ใน procstats) ซึ่งจะทำให้ LMK
เรียกคืนหน่วยความจำไม่ได้แม้ว่าบริการจะไม่ได้ใช้งานเลยก็ตาม
วิธีแก้ไข: กำหนดขอบเขตการเชื่อมโยงให้ตรงกับวงจรของคอมโพเนนต์ที่
ต้องการการเชื่อมโยงดังกล่าวอย่างเคร่งครัด หรือใช้การหมดเวลาเมื่อไม่มีการใช้งานที่เรียกใช้ unbindService() หลังจาก
ไม่มีการใช้งานเป็นระยะเวลาหนึ่ง หลีกเลี่ยงการยกเลิกการเชื่อมโยงและเชื่อมโยงใหม่ในการเรียก RPC แต่ละครั้ง ซึ่งจะทำให้เกิดการ Thrash ของกระบวนการและค่าใช้จ่ายในการตั้งค่า binder ซ้ำ ให้รวมงานเป็นชุดๆ ไว้เบื้องหลังตัวจับเวลาที่ไม่มีการใช้งานสั้นๆ (เช่น 5 ถึง 30 วินาที) แทน
การวางบริการที่มีผลผูกพันไว้ร่วมกับ UI ที่ใช้หน่วยความจำมาก
โดยค่าเริ่มต้น คอมโพเนนต์ทั้งหมดใน APK จะทำงานในกระบวนการเดียวกัน หากแอปของคุณ แสดงบริการที่มีผลผูกพันแบบเบา (เช่น NotificationListenerService ผู้ให้บริการวิดเจ็ต หรือบริการปลั๊กอินที่ระบบหรือ Launcher เชื่อมโยงด้วย) ใน กระบวนการเดียวกันกับ Activity หลัก กระบวนการทั้งหมดจะรับช่วง สถานะที่ยกระดับของบริการ (BFGS หรือ PERC โดยปกติคือ oom_score_adj ที่ 200 หรือ ต่ำกว่า)
เมื่อผู้ใช้เปิด UI ของแอป กระบวนการจะจัดสรรลำดับชั้นของมุมมองขนาดใหญ่
บิตแมปที่ถอดรหัสแล้ว และบัฟเฟอร์กราฟิก เมื่อผู้ใช้ออกจากหน้าเว็บ กระบวนการจะไม่ลดลงไปที่ CACHED (oom_score_adj ตั้งแต่ 900 ขึ้นไป) เนื่องจากการผูกบริการที่ใช้งานอยู่จะทำให้กระบวนการยังคงอยู่ในระดับสูง ซึ่งทำให้เกิดปัญหาที่ซับซ้อน 2 อย่างดังนี้
- ไม่มีการบีบอัดหน่วยความจำในเบื้องหลัง:
CachedAppOptimizerของระบบจะบีบอัดกระบวนการหลังจากเข้าสู่สถานะCACHEDเท่านั้น - ไม่มีการตัดหน่วยความจำ LRU ที่แคชไว้หรือการเรียกคืน LMK: ระบบจะไม่ส่งการเรียกกลับการตัดพื้นหลังที่เชื่อมโยงกับรายการ LRU ที่แคชไว้ (
TRIM_MEMORY_BACKGROUNDขึ้นไป) ขณะที่การผูกบริการจะทำให้กระบวนการอยู่ในสถานะที่สูงขึ้น หากแอปไม่ได้เผยแพร่ทรัพยากร UI อย่างชัดเจนในTRIM_MEMORY_UI_HIDDENหรือActivity.onStop()การจัดสรร UI สูงสุดจะยังคงอยู่ใน RAM ที่คะแนน OOM ที่มีสิทธิ์ ซึ่ง LMK จะเรียกคืนได้ยาก
วิธีแก้ไข: ล้างแคช UI, บิตแมปที่ถอดรหัสแล้ว และการอ้างอิงมุมมองอย่างชัดเจน
เมื่อ UI หยุดแสดง โดยใช้ Activity.onStop()
ComponentCallbacks2.onTrimMemory(TRIM_MEMORY_UI_HIDDEN)
Application.ActivityLifecycleCallbacks หรือ
ProcessLifecycleOwner หรือย้ายบริการที่ผูกไว้เสมอไปยังกระบวนการแยกต่างหากที่มีน้ำหนักเบาโดยใช้android:processแอตทริบิวต์ของไฟล์ Manifest เพื่อให้ระบบย้ายกระบวนการ UI หลักไปยังสถานะCACHEDเพื่อบีบอัดหรือเรียกคืนหน่วยความจำได้อย่างอิสระ
การละเว้นการแจ้งปัญหาที่สละสิทธิ์ในลำดับความสำคัญ
การเรียกใช้ bindService() ด้วย BIND_AUTO_CREATE เพียงอย่างเดียวจะโอนการจัดกำหนดการทั้งหมดและลำดับความสำคัญของหน่วยความจำของผู้โทรไปยังบริการเป้าหมาย เมื่อแอปที่ทำงานอยู่เบื้องหน้า
เชื่อมโยงกับบริการวิเคราะห์ การบันทึก หรือการดึงข้อมูลล่วงหน้าที่ทำงานอยู่เบื้องหลังโดยใช้เฉพาะ
BIND_AUTO_CREATE แอปจะเลื่อนระดับผู้ปฏิบัติงานเบื้องหลังนั้นเป็น
BTOP โดยไม่ตั้งใจ
การแก้ไข: เมื่อเชื่อมโยงกับบริการเสริมหรือบริการที่พยายามอย่างเต็มที่ซึ่งไม่จำเป็นต้องมี
ระดับการปกป้องเดียวกันกับ UI ในเบื้องหน้า ให้รวม BIND_AUTO_CREATE กับ
BIND_WAIVE_PRIORITY, BIND_NOT_FOREGROUND หรือ BIND_NOT_PERCEPTIBLE เพื่อให้
ระบบยังคงจัดการกระบวนการเป้าหมายในรายการ LRU ที่แคชไว้ได้
← ท้องถิ่น | ↑ ขึ้น | ทั่วทั้งระบบ →