ตามโพสต์นี้เราไม่ควรพึ่งพาวิธีสุดท้ายที่จะเรียกว่า ดังนั้นทำไม Java จึงรวมไว้ในภาษาการเขียนโปรแกรมเลย?
ดูเหมือนว่าการตัดสินใจที่จะรวมไว้ในภาษาการเขียนโปรแกรมใด ๆ ฟังก์ชั่นที่อาจได้รับการเรียก
ตามโพสต์นี้เราไม่ควรพึ่งพาวิธีสุดท้ายที่จะเรียกว่า ดังนั้นทำไม Java จึงรวมไว้ในภาษาการเขียนโปรแกรมเลย?
ดูเหมือนว่าการตัดสินใจที่จะรวมไว้ในภาษาการเขียนโปรแกรมใด ๆ ฟังก์ชั่นที่อาจได้รับการเรียก
คำตอบ:
ตาม Java ที่มีประสิทธิภาพของ Joshua Bloch (รุ่นที่สอง) มีสองสถานการณ์เมื่อfinalize()
เป็นประโยชน์:
หนึ่งคือการทำหน้าที่เป็น "ตาข่ายความปลอดภัย" ในกรณีที่เจ้าของวัตถุลืมที่จะเรียกวิธีการเลิกจ้างที่ชัดเจน ในขณะที่ไม่มีการรับประกันว่า finalizer จะถูกเรียกใช้ทันทีมันอาจจะดีกว่าที่จะปล่อยให้ทรัพยากรช้ากว่าที่ไม่เคยเกิดขึ้นในกรณี (หวังว่าจะหายาก) ในกรณีที่ลูกค้าไม่สามารถเรียกวิธีการยกเลิกอย่างชัดเจน แต่ finalizer ควรบันทึกคำเตือนหากพบว่าทรัพยากรไม่ได้ถูกยกเลิก
การใช้ finalizers อย่างถูกต้องตามกฎหมายครั้งที่สองจะเกี่ยวข้องกับวัตถุกับเพียร์ดั้งเดิม เพียร์พื้นเมืองคือวัตถุพื้นเมืองที่วัตถุปกติมอบหมายผ่านวิธีการเนทิฟ เนื่องจากเพียร์เนทีฟไม่ใช่วัตถุปกติตัวเก็บรวบรวมขยะจึงไม่ทราบและไม่สามารถเรียกคืนได้เมื่อเพียร์ Java ถูกเรียกคืน finalizer เป็นยานพาหนะที่เหมาะสมสำหรับการทำงานนี้โดยสมมติว่าเพียร์ดั้งเดิมไม่มีทรัพยากรที่สำคัญ หากเพียร์เนทีฟมีทรัพยากรที่ต้องยกเลิกในทันทีคลาสควรมีวิธีการยกเลิกอย่างชัดเจนตามที่อธิบายไว้ข้างต้น วิธีการเลิกจ้างควรทำทุกอย่างที่จำเป็นเพื่อให้ทรัพยากรที่สำคัญหลุดพ้น
สำหรับการอ่านเพิ่มเติมให้ดูข้อ 7 หน้า 27
Finalizers มีความสำคัญสำหรับการจัดการทรัพยากรพื้นเมือง ตัวอย่างเช่นวัตถุของคุณอาจต้องจัดสรร WidgetHandle จากระบบปฏิบัติการโดยใช้ที่ไม่ใช่ Java API หากคุณไม่ปล่อย WidgetHandle นั้นเมื่อวัตถุของคุณเป็น GC'd คุณจะต้องปล่อย WidgetHandles ออกมา
สิ่งสำคัญคือกรณี "finalizer ไม่เคยถูกเรียกว่า" แยกย่อยได้ง่าย ๆ :
ในทั้งสามกรณีนี้คุณอาจไม่ได้มีการรั่วไหลของพื้นเมือง (โดยอาศัยอำนาจตามความจริงที่ว่าโปรแกรมที่คุณไม่ได้ทำงานอีกต่อไป) หรือคุณมีการรั่วไหลที่ไม่ใช่เจ้าของภาษา (ถ้าคุณให้จัดสรรวัตถุที่มีการจัดการโดยที่พวกเขาเป็น GC'd)
คำเตือน "ไม่พึ่งพา finalizer ที่เรียกว่า" เป็นเรื่องเกี่ยวกับการไม่ใช้ finalizers สำหรับตรรกะของโปรแกรม ตัวอย่างเช่นคุณไม่ต้องการติดตามจำนวนวัตถุของคุณที่มีอยู่ในทุกอินสแตนซ์ของโปรแกรมของคุณโดยการเพิ่มตัวนับในไฟล์ที่ใดที่หนึ่งระหว่างการก่อสร้าง สรุปแล้วตัวนับไฟล์นี้อาจจะไม่กลับไปเป็น 0 นี่เป็นกรณีพิเศษของหลักการทั่วไปที่คุณไม่ควรพึ่งพาโปรแกรมของคุณที่จะยุติการทำงานตามปกติ (ไฟฟ้าขัดข้อง ฯลฯ )
สำหรับการจัดการทรัพยากรดั้งเดิมนั้นกรณีที่ตัวเรียกใช้ขั้นสุดท้ายไม่ทำงานสอดคล้องกับกรณีที่คุณไม่สนใจหากไม่ได้ทำงาน
close()
จะไม่เรียกว่าก่อนที่มันจะกลายเป็นไม่สามารถเข้าถึง)
วัตถุประสงค์ของวิธีนี้อธิบายไว้ในเอกสารประกอบ APIดังต่อไปนี้:
มันถูกเรียกใช้ถ้าและเมื่อเครื่องเสมือน Java ได้พิจารณาแล้วว่าไม่มีวิธีการใดที่วัตถุนี้สามารถเข้าถึงได้โดยเธรดใด ๆ ที่ยังไม่ตายยกเว้นเป็นผลมาจากการกระทำโดยการสรุปของวัตถุอื่น ๆ หรือชั้นเรียนที่พร้อมจะสรุป ...
วัตถุประสงค์ปกติของ
finalize
... คือการดำเนินการก่อนที่จะทำความสะอาดวัตถุที่ถูกทิ้งอย่างถาวร ตัวอย่างเช่นวิธีการสุดท้ายสำหรับวัตถุที่แสดงถึงการเชื่อมต่ออินพุต / เอาท์พุตอาจทำธุรกรรม I / O ที่ชัดเจนเพื่อทำลายการเชื่อมต่อก่อนที่วัตถุจะถูกทิ้งอย่างถาวร ...
หากคุณสนใจเพิ่มเติมในเหตุผลที่นักออกแบบภาษาเลือกที่ "ยกเลิกวัตถุอย่างถาวร" ( เก็บขยะ ) ในแบบที่อยู่นอกเหนือการควบคุมโปรแกรมเมอร์ของแอปพลิเคชัน ("เราไม่ควรพึ่งพา") สิ่งนี้ได้รับการอธิบายในคำตอบที่เกี่ยวข้อง คำถาม :
การรวบรวมขยะอัตโนมัติ ... กำจัดคลาสทั้งหมดของข้อผิดพลาดในการเขียนโปรแกรมที่ตัวควบคุม C และ C ++ คุณสามารถพัฒนาโค้ด Java ด้วยความมั่นใจว่าระบบจะพบข้อผิดพลาดมากมายได้อย่างรวดเร็วและปัญหาสำคัญจะไม่หยุดนิ่งจนกว่าจะส่งรหัสการผลิตของคุณ
ในทางกลับกันอ้างจากเอกสารทางราชการเกี่ยวกับเป้าหมายการออกแบบของ Javaนั่นคือการอ้างอิงที่เชื่อถือได้อธิบายว่าทำไมนักออกแบบภาษา Java จึงตัดสินใจด้วยวิธีนี้
สำหรับรายละเอียดเพิ่มเติมและการสนทนาภาษาผู้ไม่เชื่อเรื่องพระเจ้าของการตั้งค่านี้ดูOOSCมาตรา9.6 การจัดการหน่วยความจำอัตโนมัติ (อันที่จริงไม่เพียง แต่ส่วนนี้ แต่บทที่ 9 ทั้งหมดมีมูลค่าการอ่านถ้าคุณสนใจในสิ่งนั้น) ส่วนนี้จะเปิดขึ้นพร้อมกับคำสั่งที่ชัดเจน:
สภาพแวดล้อม OO ที่ดีควรมีกลไกการจัดการหน่วยความจำอัตโนมัติซึ่งจะตรวจจับและเรียกคืนออบเจ็กต์ที่ไม่สามารถเข้าถึงได้ช่วยให้นักพัฒนาแอปพลิเคชันมีสมาธิกับงาน - การพัฒนาแอปพลิเคชัน
การสนทนาก่อนหน้านี้ควรพอเพียงเพื่อแสดงให้เห็นว่าการให้ความสะดวกนั้นมีความสำคัญเช่นไร ในคำพูดของ Michael Schweitzer และ Lambert Strether:
โปรแกรมเชิงวัตถุที่ไม่มีการจัดการหน่วยความจำอัตโนมัตินั้นเหมือนกับหม้อหุงความดันที่ไม่มีวาล์วนิรภัย: ไม่ช้าก็เร็วสิ่งนี้จะระเบิดขึ้น!
Finalizers มีอยู่เพราะพวกเขาคาดว่าจะเป็นวิธีที่มีประสิทธิภาพในการทำให้แน่ใจว่าสิ่งต่าง ๆ ได้รับการทำความสะอาด (แม้ว่าในทางปฏิบัติแล้วพวกเขาไม่ได้) และเพราะเมื่อพวกเขาถูกประดิษฐ์ขึ้นมา - แหล่งข้อมูล) ยังไม่มี ในความเข้าใจย้อนหลัง Java น่าจะดีกว่าหากความพยายามที่ใช้ในการดำเนินการสิ่งอำนวยความสะดวก "เสร็จสิ้น" ได้ถูกใช้ไปกับวิธีการล้างอื่น ๆ แต่ก็ไม่ค่อยชัดเจนในช่วงแรกที่ Java ถูกพัฒนาขึ้น
ถ้ำ: ฉันอาจจะล้าสมัย แต่นี่เป็นความเข้าใจของฉันเมื่อไม่กี่ปีที่ผ่านมา:
โดยทั่วไปจะไม่มีการรับประกันว่าเมื่อใดที่ตัวเลือกสุดท้ายทำงาน - หรือแม้กระทั่งที่ตัวเรียกใช้เลยแม้ว่า JVM บางตัวจะช่วยให้คุณร้องขอ GC และการสรุปที่สมบูรณ์ก่อนที่โปรแกรมจะออก (ซึ่งแน่นอนว่าโปรแกรมใช้เวลานานขึ้น เพื่อออกและไม่ใช่โหมดการทำงานเริ่มต้น)
และบางคนรู้ว่า GCs ล่าช้าอย่างชัดเจนหรือหลีกเลี่ยงวัตถุ GC'ing ที่มี finalizers ด้วยความหวังว่าสิ่งนี้จะสร้างประสิทธิภาพที่ดีขึ้นในการวัดประสิทธิภาพ
น่าเสียดายที่พฤติกรรมเหล่านี้ขัดแย้งกับเหตุผลดั้งเดิมที่แนะนำให้ผู้เข้ารอบสุดท้ายและสนับสนุนให้ใช้วิธีการปิดระบบที่เรียกอย่างชัดเจนแทน
หากคุณมีวัตถุที่ต้องทำความสะอาดก่อนที่จะถูกทิ้งและถ้าคุณไม่สามารถเชื่อใจผู้ใช้ในการทำเช่นนั้นได้เครื่องมือขั้นสุดท้ายอาจยังคงมีมูลค่าการพิจารณา แต่โดยทั่วไปมีเหตุผลที่ดีที่คุณไม่เห็นบ่อยในโค้ด Java ที่ทันสมัยเหมือนที่คุณทำในตัวอย่างแรก ๆ
คู่มือสไตล์ Google Javaมีคำแนะนำปัญญาชนบางอย่างเกี่ยวกับเรื่อง:
มันเป็นเรื่องยากมาก
Object.finalize
ที่จะแทนที่เคล็ดลับ : อย่าทำ ถ้าคุณอย่างจะต้องเป็นครั้งแรกที่อ่านและทำความเข้าใจที่มีประสิทธิภาพ Javaรายการที่ 7 "Finalizers หลีกเลี่ยง" อย่างระมัดระวังและจากนั้นไม่ได้ทำมัน
PhantomReference
และReferenceQueue
แทนที่
PhantomReference
มันเป็นทางออกที่ดีกว่า Finalizers เป็นหูดที่เหลือจากวันแรกของ Java และชอบObject.clone()
และประเภทดิบเป็นส่วนหนึ่งของภาษาที่ดีที่สุดที่ถูกลืม
สถานะข้อกำหนดภาษา Java (Java SE 7) :
Finalizers ให้โอกาสในการเพิ่มทรัพยากรที่ไม่สามารถทำให้เป็นอิสระโดยอัตโนมัติโดยผู้จัดการพื้นที่จัดเก็บอัตโนมัติ ในสถานการณ์เช่นนี้เพียงแค่เรียกคืนหน่วยความจำที่ใช้โดยวัตถุจะไม่รับประกันว่าทรัพยากรที่เก็บไว้จะถูกเรียกคืน