Java เป็นภาษาคอลเล็กชั่นขยะประเภทหนึ่ง ข้อได้เปรียบของมันคือนักพัฒนาไม่จำเป็นต้องจัดการการจัดสรรหน่วยความจำโดยเจตนาซึ่งจะช่วยลดความเป็นไปได้ที่แอปพลิเคชันจะล่มเนื่องจากความผิดพลาดในการแบ่งส่วนในท้องถิ่นและป้องกันไม่ให้หน่วยความจำไม่แข็งตัวจากการบีบสแต็ก ดังนั้นรหัสที่เขียนจึงปลอดภัยกว่า
น่าเสียดายที่ยังมีการรั่วไหลเชิงตรรกะมากมายในชวาที่มีแนวโน้มที่จะรั่วไหลของหน่วยความจำ หากคุณไม่ระวังแอปพลิเคชัน Android ของคุณสามารถสูญเสียหน่วยความจำที่ไม่ได้รับการปลดปล่อยซึ่งในที่สุดจะนำไปสู่ข้อผิดพลาดของหน่วยความจำที่หมดลง
1. เหตุผลสำหรับการรั่วไหลของหน่วยความจำทั่วไปคือเมื่อการอ้างอิงทั้งหมดไปยังวัตถุได้รับการปล่อยตัววัตถุยังไม่ได้รับการปล่อยตัว (หมายเหตุของนักแปล: เคอร์เซอร์ลืมปิด ฯลฯ )
2. เหตุผลสำหรับการรั่วไหลของหน่วยความจำแบบลอจิคัลคือเมื่อแอปพลิเคชันไม่ต้องการวัตถุนี้อีกต่อไปการอ้างอิงทั้งหมดไปยังวัตถุยังไม่ได้รับการปล่อยตัว
หากคุณมีการอ้างอิงที่แข็งแกร่งไปยังวัตถุตัวเก็บขยะไม่สามารถรีไซเคิลวัตถุในหน่วยความจำได้
ในการพัฒนา Android ปัญหาการรั่วไหลของหน่วยความจำที่เป็นไปได้มากที่สุดคือบริบท ตัวอย่างเช่นบริบทของกิจกรรมมีการอ้างอิงหน่วยความจำจำนวนมากเช่นลำดับชั้นดูและทรัพยากรอื่น ๆ เมื่อบริบทรั่วไหลออกมาก็หมายถึงการรั่วไหลของวัตถุทั้งหมดที่ชี้ไป เครื่อง Android มีหน่วยความจำที่ จำกัด และการรั่วไหลของหน่วยความจำมากเกินไปสามารถนำไปสู่ OOM ได้อย่างง่ายดาย
การตรวจจับการรั่วไหลของหน่วยความจำเชิงตรรกะนั้นต้องมีการตัดสินแบบอัตนัยโดยเฉพาะวงจรชีวิตของวัตถุไม่ชัดเจน โชคดีที่กิจกรรมมีวงจรชีวิตที่ชัดเจนและเป็นเรื่องง่ายที่จะหาสาเหตุของการรั่วไหล กิจกรรม ondestroy () ถือเป็นจุดสิ้นสุดของชีวิตกิจกรรม ในทางโปรแกรมควรถูกทำลายหรือระบบ Android จำเป็นต้องรีไซเคิลหน่วยความจำนี้ (หมายเหตุของนักแปล: เมื่อหน่วยความจำไม่เพียงพอ Android จะรีไซเคิลกิจกรรมที่มองไม่เห็น)
หากวิธีการนี้ดำเนินการยังคงมีการอ้างอิงที่แข็งแกร่งถึงกิจกรรมในสแต็กและตัวเก็บขยะไม่สามารถทำเครื่องหมายเป็นหน่วยความจำรีไซเคิลและจุดประสงค์ดั้งเดิมของเราคือการรีไซเคิล!
ผลที่ได้คือกิจกรรมมีชีวิตอยู่นอกวงจรชีวิต
กิจกรรมเป็นวัตถุที่มีน้ำหนักมากและควรได้รับการจัดการโดยระบบ Android อย่างไรก็ตามการรั่วไหลของหน่วยความจำเชิงตรรกะมักจะเกิดขึ้นโดยไม่ได้ตั้งใจ (หมายเหตุของนักแปล: ฉันเคยลองทำกิจกรรมที่ทำให้หน่วยความจำรั่ว 20 ม.) ใน Android มีเพียงสองกับดักที่นำไปสู่การรั่วไหลของหน่วยความจำที่อาจเกิดขึ้น:
ตัวแปรคงที่ของกระบวนการทั่วโลก (Process-Global) สัตว์ประหลาดตัวนี้ที่ไม่สนใจสถานะของแอปพลิเคชันและมีการอ้างอิงที่แข็งแกร่งไปยังกิจกรรม
หัวข้อที่อาศัยอยู่นอกวงจรชีวิตของกิจกรรม ไม่มีการอ้างอิงที่แข็งแกร่งไปยังกิจกรรม
ตรวจสอบว่าคุณได้พบกับสถานการณ์ต่อไปนี้หรือไม่
1. กิจกรรมคงที่
ตัวแปรกิจกรรมคงที่ถูกกำหนดไว้ในคลาสและอินสแตนซ์กิจกรรมที่กำลังรันอยู่ในปัจจุบันถูกกำหนดให้กับตัวแปรคงที่นี้
หากตัวแปรคงที่นี้ไม่ได้ถูกล้างหลังจากสิ้นสุดวงจรชีวิตกิจกรรมมันจะทำให้หน่วยความจำรั่วไหล เนื่องจากตัวแปรคงที่ทำงานผ่านวงจรชีวิตของแอปพลิเคชันนี้กิจกรรมที่รั่วไหลจะมีอยู่เสมอในกระบวนการสมัครและจะไม่ถูกรวบรวมโดยนักสะสมขยะ
กิจกรรมคงที่ เป็นโมฆะ setStaticActivity () {activity = this;} ดู sabutton = findViewById (r.id.sa_button); sabutton.setonclicklistener (มุมมองใหม่ onclicklistener () {@Override สาธารณะ2. มุมมองแบบคงที่
สถานการณ์ที่คล้ายกันสามารถเกิดขึ้นได้ในโหมดซิงเกิลตันและหากมีการใช้กิจกรรมบ่อยครั้งมันก็เป็นประโยชน์ในการบันทึกอินสแตนซ์ในหน่วยความจำ ดังที่ได้กล่าวไว้ก่อนหน้านี้การบังคับใช้วงจรชีวิตของกิจกรรมนั้นค่อนข้างอันตรายและไม่จำเป็นและไม่สามารถทำได้
กรณีพิเศษ: หากการเริ่มต้นมุมมองนั้นใช้ทรัพยากรจำนวนมากและยังคงไม่เปลี่ยนแปลงในระหว่างวงจรชีวิตของกิจกรรมก็สามารถเปลี่ยนเป็นแบบคงที่และโหลดลงในมุมมอง Hierachy ด้วยวิธีนี้เมื่อกิจกรรมถูกทำลายทรัพยากรควรได้รับการปล่อยตัว (หมายเหตุของนักแปล: หน่วยความจำไม่ได้เปิดตัวในรหัสตัวอย่างเพียงแค่แสดงมุมมองแบบคงที่นี้ แต่ก็ยังไม่แนะนำให้ใช้วิธีการดูแบบคงที่)
มุมมองแบบคงที่; โมฆะ setStaticView () {view = findViewById (r.id.sv_button);} ดู svbutton = findViewById (r.id.sv_button); svbutton.setonclicklistener -3. ชั้นเรียน
ดำเนินการต่อโดยสมมติว่ามีคลาสภายในในกิจกรรมการทำเช่นนั้นสามารถปรับปรุงความสามารถในการอ่านและการห่อหุ้ม หากเราสร้างคลาสภายในและอ้างอิงถึงตัวแปรคงที่ขอแสดงความยินดีการรั่วไหลของหน่วยความจำอยู่ไม่ไกลจากคุณ (หมายเหตุของนักแปล: ว่างเปล่าเมื่อถูกทำลาย, um)
วัตถุคงที่ส่วนตัวภายใน; เป็นโมฆะ createInnerClass () {คลาส innerClass {} inner = new innerClass ();} ดู icButton = findViewById (r.id.ic_button); icbutton.setonClickListener -ข้อดีอย่างหนึ่งของคลาสภายในคือพวกเขาสามารถเข้าถึงคลาสภายนอก น่าเสียดายที่เหตุผลสำหรับการรั่วไหลของหน่วยความจำคือคลาสภายในมีการอ้างอิงที่แข็งแกร่งไปยังอินสแตนซ์คลาสภายนอก
4. ชั้นเรียนที่ไม่ระบุชื่อ
ในทำนองเดียวกันคลาสที่ไม่ระบุชื่อยังคงอ้างอิงถึงคลาสภายนอก ดังนั้นการรั่วไหลของหน่วยความจำจึงเกิดขึ้นได้ง่ายเมื่อคุณกำหนด asynctsk ที่ไม่ระบุชื่อในกิจกรรมของคุณ เมื่องานอะซิงโครนัสดำเนินการงานที่ใช้เวลานานในพื้นหลังกิจกรรมจะถูกทำลายอย่างน่าเสียดาย (หมายเหตุของนักแปล: ผู้ใช้ออกการรีไซเคิลระบบ) อินสแตนซ์กิจกรรมนี้จัดขึ้นโดย Asynctask จะไม่ถูกนำไปรีไซเคิลโดยนักสะสมขยะจนกว่างานอะซิงโครนัสจะเสร็จสมบูรณ์
เป็นโมฆะ startasynctask () {ใหม่ asynctask <void, void, void> () {@Override void doinbackground (void ... params) {ในขณะที่ (จริง); }} .execute ();} super.oncreate (savedinstancestate); setContentView (r.layout.Activity_Main); ดู AicButton = findViewById (r.id.at_button); aicButton.setonclicklistener startasynctask ();5. handler
ด้วยโทเค็นเดียวกันให้กำหนด runnable ที่ไม่ระบุชื่อและดำเนินการด้วยตัวจัดการคลาสที่ไม่ระบุชื่อ คลาสภายในที่เรียกใช้ได้จะมีการอ้างอิงโดยนัยไปยังคลาสภายนอกและจะถูกส่งผ่านไปยังข้อความของคิวข้อความตัวจัดการ อินสแตนซ์กิจกรรมจะไม่ถูกทำลายจนกว่าข้อความจะถูกประมวลผลส่งผลให้หน่วยความจำรั่วไหล
เป็นโมฆะ createHandler () {handler ใหม่ () {@Override โมฆะสาธารณะ handleMessage (ข้อความข้อความ) {super.handleMessage (ข้อความ); }} .postDelayed (ใหม่ runnable () {@Override public void run () {ในขณะที่ (จริง);}}, long.max_value >> 1);} ดู hbutton = findViewById (r.id.h_button); onclick (ดู v) {createHandler ();6. เธรด
เราแสดงการรั่วไหลของหน่วยความจำอีกครั้งผ่านเธรดและ timertask
เป็นโมฆะ SpawnThread () {เธรดใหม่ () {@Override โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (จริง); }} .start ();} ดู tbutton = findViewById (r.id.t_button); tbutton.setonclicklistener (มุมมองใหม่ onclicklistener () {@Override โมฆะสาธารณะ onClick (ดู v) {Spawnthread (); nextactivity ();7. TimerTask
ตราบใดที่มันเป็นตัวอย่างของคลาสที่ไม่ระบุชื่อไม่ว่าจะอยู่ในเธรดของคนงานหรือไม่ก็จะมีการอ้างอิงถึงกิจกรรมส่งผลให้หน่วยความจำรั่วไหล
โมฆะ scheduleTimer () {ตัวจับเวลาใหม่ (). กำหนดเวลา (ใหม่ timertask () {@Override โมฆะสาธารณะเรียกใช้ () {ในขณะที่ (จริง);}}, long.max_value >> 1);} ดู ttbutton = findViewById (r.id.tt_button); @Override โมฆะสาธารณะ onclick (ดู V) {ScheduleTimer ();8. ผู้จัดการ SENSOR
ในที่สุดบริการระบบสามารถรับได้ผ่านบริบท GetSystemService (ชื่อ int) บริการเหล่านี้ทำงานในกระบวนการของตนช่วยให้แอปพลิเคชันจัดการงานพื้นหลังและการโต้ตอบกับฮาร์ดแวร์ หากคุณต้องการใช้บริการเหล่านี้คุณสามารถลงทะเบียนผู้ฟังซึ่งจะทำให้บริการอ้างอิงถึงบริบท หากผู้ฟังเหล่านี้ไม่ได้ออกจากระบบเมื่อกิจกรรมถูกทำลายมันจะทำให้เกิดการรั่วไหลของหน่วยความจำ
เป็นโมฆะ registerListener () {SensorManager SensorManager = (SensorManager) GetSystemService (Sensor_Service); เซ็นเซอร์เซ็นเซอร์ = sensorManager.getDefaultSensor (sensor.type_all); SensorManager.RegisterListener (นี่, เซ็นเซอร์, SensorManager.Sensor_delay_fastest);} ดู smbutton = findViewById (r.id.sm_button); smbutton.setonclicklistener -สรุป
เมื่อเห็นตัวอย่างมากมายที่สามารถนำไปสู่การรั่วไหลของหน่วยความจำมันเป็นเรื่องง่ายที่จะกินความทรงจำทั้งหมดของโทรศัพท์ของคุณการรวบรวมขยะและการประมวลผลบ่อยขึ้นและแม้ในกรณีที่เลวร้ายที่สุดมันจะนำไปสู่ oom การดำเนินการรวบรวมขยะมีราคาแพงและสามารถนำไปสู่ความล่าช้าที่มองเห็นได้ ดังนั้นให้ความสนใจกับห่วงโซ่อ้างอิงที่จัดขึ้นเมื่ออินสแตนซ์และมักจะทำการตรวจสอบการรั่วไหลของหน่วยความจำ