เหตุใดกระบวนทัศน์ของผู้ทำลายล้างวัตถุในภาษาที่รวบรวมขยะจึงหายไปอย่างแพร่หลาย?


27

กำลังมองหาข้อมูลเชิงลึกเกี่ยวกับการตัดสินใจเกี่ยวกับการออกแบบภาษาที่รวบรวมขยะ บางทีผู้เชี่ยวชาญด้านภาษาสามารถสอนฉัน ฉันมาจากพื้นหลัง C ++ ดังนั้นพื้นที่นี้ทำให้ฉันงุนงง

ดูเหมือนว่าภาษาที่รวบรวมขยะสมัยใหม่เกือบทั้งหมดที่มีการสนับสนุนวัตถุ OOPy เช่น Ruby, Javascript / ES6 / ES7, Actionscript, Lua และอื่น ๆ จะไม่ใช้ destructor / finalize paradigm Python ดูเหมือนจะเป็นคนเดียวที่มีclass __del__()วิธีการ ทำไมนี้ มีข้อ จำกัด ด้านการทำงาน / ทางทฤษฎีในภาษาที่มีการรวบรวมขยะอัตโนมัติซึ่งป้องกันการใช้งานที่มีประสิทธิภาพของวิธีการ destructor / จบบนวัตถุ?

ฉันพบว่าภาษาเหล่านี้ไม่ได้พิจารณาถึงความทรงจำเป็นอย่างยิ่งเพราะเป็นเพียงการจัดการทรัพยากร เกี่ยวกับซ็อกเก็ตตัวจัดการไฟล์สถานะแอปพลิเคชัน หากปราศจากความสามารถในการใช้ตรรกะที่กำหนดเองเพื่อล้างทรัพยากรที่ไม่ใช่หน่วยความจำและสถานะในการสรุปวัตถุฉันต้องทำให้แอปพลิเคชั่นของฉันมีmyObject.destroy()สไตล์การโทรที่กำหนดเองวางตรรกะการล้างข้อมูลนอก "คลาส" ของฉัน การประยุกต์ใช้กับการรั่วไหลของทรัพยากรเนื่องจากข้อผิดพลาดของมนุษย์มากกว่าจะถูกจัดการโดย gc โดยอัตโนมัติ

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

ปรับปรุง:

อาจเป็นวิธีที่ดีกว่าในการใช้คำถามของฉัน:

ทำไมภาษาถึงมีแนวคิดในตัวของอินสแตนซ์ของวัตถุที่มีโครงสร้างคลาสหรือคล้ายคลาสพร้อมกับการสร้างอินสแตนซ์ที่กำหนดเอง (constructors) แต่ยังละเว้นฟังก์ชันการทำลาย / การทำให้เสร็จสมบูรณ์อย่างสมบูรณ์ ภาษาที่เสนอการรวบรวมขยะอัตโนมัติดูเหมือนจะเป็นตัวเลือกที่สำคัญในการสนับสนุนการทำลายวัตถุ / การจัดทำขั้นสุดท้ายตามที่พวกเขารู้ด้วยความมั่นใจ 100% เมื่อวัตถุไม่ได้ใช้งานอีกต่อไป แต่ภาษาเหล่านั้นส่วนใหญ่ไม่สนับสนุน

ฉันไม่คิดว่ามันเป็นกรณีที่ destructor อาจไม่เคยถูกเรียกเช่นนั้นจะเป็นหน่วยความจำหลักรั่วซึ่ง gcs ที่ออกแบบมาเพื่อหลีกเลี่ยง ฉันเห็นข้อโต้แย้งที่เป็นไปได้ว่า destructor / finalizer อาจไม่ได้รับการเรียกจนกว่าจะถึงเวลาที่แน่นอนในอนาคต แต่นั่นก็ไม่ได้หยุด Java หรือ Python จากการสนับสนุนการทำงาน

อะไรคือเหตุผลหลักในการออกแบบภาษาที่ไม่รองรับรูปแบบของการสรุปวัตถุ


9
อาจเป็นเพราะfinalize/ destroyเป็นเรื่องโกหก? ไม่มีการรับประกันว่าจะถูกประหารชีวิต และแม้ว่าคุณจะไม่ทราบว่าเมื่อใด (ได้รับการรวบรวมขยะอัตโนมัติ) และหากบริบทที่จำเป็นยังคงมีอยู่ (อาจถูกรวบรวมไปแล้ว) ดังนั้นจึงปลอดภัยกว่าที่จะตรวจสอบสถานะที่สอดคล้องในวิธีอื่นและอาจต้องการบังคับให้โปรแกรมเมอร์ทำเช่นนั้น
กราฟิลส์

1
ฉันคิดว่าคำถามนี้เป็นเส้นเขตแดนที่น่าสนใจ เป็นคำถามออกแบบภาษาโปรแกรมประเภทที่เราต้องการสร้างความบันเทิงหรือเป็นคำถามสำหรับเว็บไซต์ที่มุ่งเน้นการเขียนโปรแกรมหรือไม่? กรุณาโหวตชุมชน
กราฟิลส์

14
มันเป็นคำถามที่ดีในการออกแบบ PL ลองดูสิ
Andrej Bauer

3
นี่ไม่ใช่ความแตกต่างแบบสแตติก / ไดนามิก ภาษาแบบสแตติกจำนวนมากไม่มี finalizers ในความเป็นจริงแล้วภาษาที่ไม่ใช่ตัวสุดท้ายในชนกลุ่มน้อยไม่ใช่หรือ
Andrej Bauer

1
คิดว่ามีคำถามที่นี่ ... มันจะดีกว่าถ้าคุณนิยามคำศัพท์อีกเล็กน้อย java มีบล็อกในที่สุดที่ไม่ได้ผูกกับการทำลายวัตถุ แต่วิธีการออกจาก นอกจากนี้ยังมีวิธีอื่นในการจัดการทรัพยากร เช่นใน java กลุ่มการเชื่อมต่อสามารถจัดการกับการเชื่อมต่อที่ไม่ได้ใช้ [x] จำนวนครั้งและเรียกคืนพวกเขา ไม่หรูหรา แต่ใช้งานได้ ส่วนหนึ่งของคำตอบสำหรับคำถามของคุณคือการเก็บขยะเป็นแบบไม่ จำกัด กระบวนการไม่ได้เกิดขึ้นทันทีและไม่ได้ถูกขับเคลื่อนโดยวัตถุที่ไม่ได้ใช้งานอีกต่อไป แต่เกิดจากข้อ จำกัด / เพดานของหน่วยความจำ
vzn

คำตอบ:


10

รูปแบบที่คุณกำลังพูดถึงที่วัตถุรู้วิธีทำความสะอาดทรัพยากรของพวกเขาแบ่งออกเป็นสามประเภทที่เกี่ยวข้อง อย่าแช่งผู้ทำลายล้างด้วยfinalizers - มีเพียงคนเดียวที่เกี่ยวข้องกับการเก็บขยะ:

  • รูปแบบ finalizer : วิธีการทำความสะอาดโดยอัตโนมัติประกาศกำหนดโดยโปรแกรมเมอร์เรียกโดยอัตโนมัติ

    Finalizers จะถูกเรียกโดยอัตโนมัติก่อนการจัดสรรคืนโดยตัวรวบรวมขยะ คำนี้ใช้หากอัลกอริทึมการรวบรวมขยะที่ใช้สามารถกำหนดวัฏจักรชีวิตของวัตถุได้

  • รูปแบบ destructor : วิธีการทำความสะอาดโดยอัตโนมัติประกาศกำหนดโดยโปรแกรมเมอร์เรียกโดยอัตโนมัติบางครั้งเท่านั้น

    Destructors สามารถเรียกใช้โดยอัตโนมัติสำหรับวัตถุที่จัดสรรสแต็ก (เนื่องจากอายุการใช้งานของวัตถุกำหนดขึ้นอย่างแน่นอน) แต่จะต้องเรียกอย่างชัดเจนในทุกเส้นทางการดำเนินการที่เป็นไปได้สำหรับวัตถุที่จัดสรรฮีป (เพราะอายุการใช้งานของวัตถุนั้นเป็น

  • รูปแบบ disposer : วิธีการล้างข้อมูลประกาศกำหนดและเรียกโดยโปรแกรมเมอร์

    โปรแกรมเมอร์สร้างวิธีการกำจัดและเรียกมันว่าตัวเอง - นี่คือmyObject.destroy()วิธีการที่คุณกำหนดเอง หากต้องการการกำจัดอย่างสมบูรณ์จะต้องเรียกใช้ตัวกำจัดบนเส้นทางการปฏิบัติที่เป็นไปได้ทั้งหมด

Finalizers เป็นหุ่นที่คุณกำลังมองหา

รูปแบบขั้นสุดท้าย (รูปแบบที่คำถามของคุณถาม) เป็นกลไกในการเชื่อมโยงวัตถุกับทรัพยากรระบบ (ซ็อกเก็ตตัวอธิบายไฟล์ ฯลฯ ) สำหรับการเรียกคืนโดยรวมโดยตัวรวบรวมขยะ แต่ขั้นตอนสุดท้ายคือพื้นฐานที่ความเมตตาของอัลกอริทึมการเก็บขยะที่ใช้งานอยู่

พิจารณาสมมติฐานของคุณนี้:

ภาษาที่ให้บริการการเก็บขยะอัตโนมัติ ... รู้ด้วยความมั่นใจ 100% เมื่อวัตถุไม่ได้ใช้งานอีกต่อไป

เทคนิคเท็จ (ขอบคุณ @babou) การรวบรวมขยะเป็นพื้นฐานเกี่ยวกับหน่วยความจำไม่ใช่วัตถุ หากหรือเมื่ออัลกอริทึมการรวบรวมตระหนักถึงความทรงจำของวัตถุที่ไม่ได้ใช้งานอีกต่อไปขึ้นอยู่กับอัลกอริทึมและ (อาจ) วิธีที่วัตถุของคุณอ้างถึงกันและกัน มาพูดถึงนักสะสมขยะสองประเภทกัน มีหลายวิธีในการแก้ไขและเพิ่มเทคนิคเหล่านี้ให้เป็นพื้นฐาน:

  1. ติดตาม GC หน่วยความจำการติดตามเหล่านี้ไม่ใช่วัตถุ นอกจากจะเพิ่มให้ทำเช่นนั้นพวกเขาจะไม่รักษากลับอ้างอิงถึงวัตถุจากหน่วยความจำ ยกเว้นว่าจะเพิ่มมากขึ้น GCs เหล่านี้จะไม่ทราบว่าจะสามารถสรุปวัตถุได้แม้ว่าพวกเขาจะรู้เมื่อหน่วยความจำไม่สามารถเข้าถึงได้ ดังนั้นการโทรครั้งสุดท้ายจึงไม่รับประกัน

  2. อ้างอิง GC วัตถุเหล่านี้ใช้เพื่อติดตามหน่วยความจำ พวกมันจำลองการเข้าถึงของวัตถุด้วยกราฟอ้างอิงโดยตรง หากมีวัฏจักรในกราฟอ้างอิงวัตถุของคุณวัตถุทั้งหมดในวัฏจักรจะไม่มีตัวเรียกรอบสุดท้าย (จนกว่าจะมีการยกเลิกโปรแกรมอย่างชัดเจน) ไม่รับประกันการโทรครั้งสุดท้าย

TLDR

การรวบรวมขยะเป็นเรื่องยากและหลากหลาย ไม่สามารถรับประกันการโทรครั้งสุดท้ายก่อนสิ้นสุดโปรแกรม


คุณถูกต้องว่านี่ไม่ใช่สแตติก v. แบบไดนามิก มันเป็นปัญหาของภาษาที่รวบรวมขยะ การรวบรวมขยะเป็นปัญหาที่ซับซ้อนและอาจเป็นสาเหตุหลักเนื่องจากมีหลายกรณีที่ต้องพิจารณา (เช่นจะเกิดอะไรขึ้นถ้าตรรกะในfinalize()สาเหตุที่ทำให้วัตถุถูกทำความสะอาดเพื่ออ้างอิงอีกครั้ง?) อย่างไรก็ตามการที่ไม่สามารถรับประกันว่า finalizer จะถูกเรียกใช้ก่อนที่การยกเลิกโปรแกรมจะไม่หยุด Java จากการสนับสนุน การไม่บอกว่าคำตอบของคุณไม่ถูกต้อง แต่อาจไม่สมบูรณ์ ยังคงโพสต์ที่ดีมาก ขอขอบคุณ.
dbcb

ขอบคุณสำหรับความคิดเห็น. ต่อไปนี้เป็นความพยายามในการตอบคำถามของฉันโดยการละเว้น finalizers อย่างชัดเจนภาษาบังคับให้ผู้ใช้จัดการทรัพยากรของตนเอง สำหรับปัญหาหลายประเภทนั่นอาจเป็นข้อเสีย โดยส่วนตัวฉันชอบตัวเลือกของ Java เพราะฉันมีพลังในการเข้ารอบสุดท้ายและไม่มีอะไรหยุดยั้งฉันจากการเขียนและใช้ disposer ของฉันเอง Java กำลังพูดว่า "เฮ้โปรแกรมเมอร์คุณไม่ใช่คนงี่เง่าดังนั้นนี่เป็นตัวสุดท้าย
kdbanman

1
อัปเดตคำถามเดิมของฉันเพื่อสะท้อนว่าข้อตกลงนี้มีภาษาที่รวบรวมขยะ ยอมรับคำตอบของคุณ ขอบคุณที่สละเวลาตอบ
dbcb

ยินดีที่ได้ช่วย. การชี้แจงความคิดเห็นของฉันทำให้คำตอบของฉันชัดเจนขึ้นหรือไม่?
kdbanman

2
ดี. สำหรับฉันคำตอบที่แท้จริงคือภาษาที่เลือกที่จะไม่นำไปใช้เนื่องจากค่าที่รับรู้ไม่ได้มีค่าเกินกว่าปัญหาของการใช้งานฟังก์ชัน มันเป็นไปไม่ได้ (ตามที่ Java และ Python สาธิต) แต่มีการแลกเปลี่ยนที่หลายภาษาเลือกที่จะไม่ทำ
dbcb

5

โดยสังเขป

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

บทนำ

ฉันคิดว่าสิ่งที่คุณถามคือสาเหตุที่ภาษาที่รวบรวมขยะไม่ได้จัดการกับการทำลาย / การสรุปโดยอัตโนมัติภายในกระบวนการรวบรวมขยะตามที่ระบุไว้ในหมายเหตุ

ฉันพบว่าภาษาเหล่านี้ไม่ได้พิจารณาถึงความทรงจำเป็นอย่างยิ่งเพราะเป็นเพียงการจัดการทรัพยากรที่คุ้มค่า เกี่ยวกับซ็อกเก็ตตัวจัดการไฟล์สถานะแอปพลิเคชัน

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

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

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

ดังนั้นจึงเป็นการดีกว่าที่จะพึ่งพาการรวบรวมขยะอัตโนมัติเพื่อเรียกคืนทรัพยากร แต่มีสองประเด็น:

  • เทคนิคการรวบรวมขยะบางอย่างจะอนุญาตให้มีหน่วยความจำรั่วที่ป้องกันการเรียกคืนทรัพยากรอย่างเต็มรูปแบบ สิ่งนี้เป็นที่รู้จักกันดีสำหรับการนับการอ้างอิงอ้างอิง GC แต่อาจปรากฏขึ้นสำหรับเทคนิค GC อื่น ๆ เมื่อใช้บางองค์กรข้อมูลโดยไม่สนใจ (จุดไม่ได้กล่าวถึงที่นี่)

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

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

การอ้างอิงนักสะสมขยะนับ

การนับการอ้างอิงเป็นเทคนิคการรวบรวมขยะที่ไม่สามารถจัดการกับวงจรได้อย่างถูกต้อง มันจะอ่อนแอในการทำลายโครงสร้างที่ล้าสมัยและเรียกคืนทรัพยากรอื่น ๆ เพียงเพราะอ่อนแอในการเรียกคืนหน่วยความจำ แต่ finalizers สามารถใช้งานได้ง่ายที่สุดด้วยการอ้างอิงการเก็บขยะนับ (GC) เนื่องจากการนับ ref- GC จะ reclaims โครงสร้างเมื่อจำนวนการอ้างอิงของมันลดลงถึง 0 ซึ่งเป็นที่อยู่ของมันรู้พร้อมกับประเภทของมันทั้งแบบคงที่ หรือแบบไดนามิก ดังนั้นจึงเป็นไปได้ที่จะเรียกคืนหน่วยความจำอย่างแม่นยำหลังจากใช้งาน finalizer ที่เหมาะสมและการเรียกซ้ำกระบวนการบนวัตถุที่ชี้ทั้งหมด (อาจผ่านขั้นตอนการสรุป)

โดยสรุปแล้วขั้นตอนสุดท้ายนั้นง่ายต่อการนำไปใช้กับ Ref Counting GC แต่ทนทุกข์ทรมานจาก "ความไม่สมบูรณ์" ของ GC นั้นเนื่องจากโครงสร้างวงกลมเพื่อให้แม่นยำในระดับเดียวกับที่หน่วยความจำได้รับการฟื้นฟู กล่าวอีกนัยหนึ่งเมื่อนับการอ้างอิงหน่วยความจำจะได้รับการจัดการอย่างไม่ดีเท่ากับทรัพยากรอื่น ๆเช่นซ็อกเก็ตการจัดการไฟล์เป็นต้น

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

ติดตามนักสะสมขยะ

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

ปัญหาคือใบแจ้งยอดของคุณ (ในตอนท้ายของคำถาม):

ภาษาที่เสนอการรวบรวมขยะอัตโนมัติดูเหมือนจะเป็นตัวเลือกที่สำคัญในการสนับสนุนการทำลายวัตถุ / การจัดทำขั้นสุดท้ายตามที่พวกเขารู้ด้วยความมั่นใจ 100% เมื่อวัตถุไม่ได้ใช้งานอีกต่อไป

เทคนิคไม่ถูกต้องสำหรับการติดตามตัวสะสม

สิ่งที่เป็นที่รู้จักกันด้วยความมั่นใจ 100% ว่าจะเป็นอะไรส่วนของหน่วยความจำไม่ได้อยู่ในการใช้งาน (แม่นยำยิ่งขึ้นก็ควรจะกล่าวว่าพวกเขาไม่สามารถเข้าถึงได้อีกต่อไปเพราะบางส่วนที่ไม่สามารถใช้งานได้ตามตรรกะของโปรแกรมจะยังคงใช้งานอยู่หากยังมีตัวชี้ที่ไร้ประโยชน์ต่อพวกเขาในโปรแกรม ข้อมูล.) แต่การประมวลผลต่อโครงสร้างที่เหมาะสมและมีความจำเป็นที่จะรู้ว่าสิ่งที่วัตถุที่ไม่ได้ใช้อาจได้รับการจัดเก็บไว้ในเหล่านี้ตอนนี้ชิ้นส่วนที่ไม่ได้ใช้ของหน่วยความจำ ไม่สามารถระบุได้จากสิ่งที่เป็นที่รู้จักของโปรแกรมเนื่องจากโปรแกรมไม่ได้เชื่อมต่อกับส่วนต่าง ๆ ของหน่วยความจำเหล่านี้อีกต่อไป

ดังนั้นหลังจากผ่านการรวบรวมขยะคุณจะเหลือเศษชิ้นส่วนของหน่วยความจำที่มีวัตถุที่ไม่ได้ใช้งานอีกต่อไป แต่มีวิธีที่จะไม่ทราบว่าวัตถุเหล่านี้คืออะไรเพื่อใช้การสรุปที่ถูกต้อง นอกจากนี้หากตัวติดตามการติดตามเป็นประเภท mark-and-sweep อาจเป็นไปได้ว่าบางส่วนของชิ้นส่วนอาจมีวัตถุที่ผ่านการสรุปใน GC Pass ก่อนหน้าแล้ว แต่ไม่ได้ใช้เนื่องจากเหตุผลในการแยกส่วน อย่างไรก็ตามสิ่งนี้สามารถจัดการได้โดยใช้การพิมพ์แบบขยายเพิ่มเติม

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

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

อีกประเด็นหนึ่งคือค่าโสหุ้ยเวลาและพื้นที่อาจเกี่ยวข้องกับการเรียกใช้โค้ดโปรแกรมไม่ใช่เฉพาะการประมวลผล GC

ฉันไม่สามารถให้คำตอบที่แม่นยำมากขึ้นโดยชี้ไปที่ปัญหาเฉพาะเพราะฉันไม่ทราบรายละเอียดของภาษาที่คุณระบุ ในกรณีของ C การพิมพ์เป็นปัญหาที่ยากมากที่นำไปสู่การพัฒนานักสะสมแบบอนุรักษ์นิยม ฉันเดาว่าจะมีผลกระทบต่อ C ++ เช่นกัน แต่ฉันไม่มีความเชี่ยวชาญใน C ++ ดูเหมือนว่าจะได้รับการยืนยันจาก Hans Boehmซึ่งได้ทำการวิจัยเกี่ยวกับ GC อนุรักษ์นิยมมาก Conservative GC ไม่สามารถเรียกคืนหน่วยความจำที่ไม่ได้ใช้ทั้งหมดอย่างเป็นระบบได้อย่างแม่นยำเนื่องจากอาจไม่มีข้อมูลประเภทที่แม่นยำกับข้อมูล ด้วยเหตุผลเดียวกันนี้จึงไม่สามารถใช้ขั้นตอนการสรุปอย่างเป็นระบบได้

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

1 - นี่เป็นการนำเสนอที่เป็นนามธรรมของการติดตามการรวบรวม (รวมทั้งการคัดลอกและการทำเครื่องหมายและกวาด GC) สิ่งต่าง ๆ ตามประเภทของตัวติดตามการติดตามและการสำรวจส่วนที่ไม่ได้ใช้ของหน่วยความจำจะแตกต่างกันขึ้นอยู่กับว่า ใช้การกวาด


คุณให้รายละเอียดมากมายกับการเก็บขยะ อย่างไรก็ตามคำตอบของคุณไม่ได้ขัดแย้งกับฉันจริงๆ - บทคัดย่อและ TLDR ของฉันกำลังพูดในสิ่งเดียวกัน และสำหรับสิ่งที่คุ้มค่าคำตอบของฉันใช้การนับ GC อ้างอิงเป็นตัวอย่างไม่ใช่ "อคติที่แข็งแกร่ง"
kdbanman

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

@kdbanman ปัญหาก็คือว่าฉันกำลังพูดถึงคุณทั้งคู่ตั้งแต่คำตอบของคุณยืนเป็นข้อมูลอ้างอิง คุณไม่สามารถใช้การนับการอ้างอิงเป็นตัวอย่างกระบวนทัศน์เนื่องจากมันเป็น GC ที่อ่อนแอซึ่งไม่ค่อยใช้ในภาษา (ตรวจสอบภาษาที่อ้างโดย OP) ซึ่งการเพิ่ม finalizers จะง่ายจริง ๆ (แต่มีการใช้งาน จำกัด ) การติดตามการสะสมมักจะใช้ แต่ Finalizers นั้นยากที่จะติดมันเพราะไม่รู้ว่าวัตถุที่กำลังจะตาย (ตรงกันข้ามกับคำแถลงที่คุณพิจารณาว่าถูกต้อง) ความแตกต่างระหว่างการพิมพ์แบบคงที่และแบบไดนามิกนั้นไม่เกี่ยวข้องโดยการพิมพ์แบบไดนามิกของแหล่งข้อมูลเป็นสิ่งจำเป็น
babou

@kdbanman เกี่ยวกับคำศัพท์มันมีประโยชน์โดยทั่วไปเพราะมันสอดคล้องกับสถานการณ์ที่แตกต่างกัน แต่ที่นี่ไม่ได้ช่วยเนื่องจากคำถามเกี่ยวกับการถ่ายโอนการสรุปไปยัง GC GC พื้นฐานควรจะทำลายเท่านั้น สิ่งที่จำเป็นคือคำศัพท์ที่แยกความแตกต่างgetting memory recycledที่ฉันเรียกใช้reclamationและทำการล้างข้อมูลก่อนหน้านั้นเช่นเรียกคืนทรัพยากรอื่น ๆ หรือปรับปรุงตารางวัตถุบางอย่างที่ฉันเรียกfinalizationใช้ สิ่งเหล่านี้ดูเหมือนจะเกี่ยวกับประเด็นที่เกี่ยวข้อง แต่ฉันอาจพลาดประเด็นในคำศัพท์ของคุณซึ่งเป็นเรื่องใหม่สำหรับฉัน
babou

1
ขอบคุณ @kdbanman, babou การอภิปรายที่ดี ฉันคิดว่าโพสต์ทั้งสองของคุณครอบคลุมจุดที่คล้ายกัน ในขณะที่คุณทั้งคู่ชี้ให้เห็นประเด็นหลักดูเหมือนจะเป็นหมวดหมู่ของตัวรวบรวมขยะที่ใช้ในการทำงานของภาษา ฉันพบบทความนี้ซึ่งล้างความเข้าใจผิดบางประการสำหรับฉัน ดูเหมือนว่า gcs ที่มีประสิทธิภาพมากขึ้นจะจัดการกับหน่วยความจำดิบระดับต่ำเท่านั้นซึ่งทำให้ประเภทวัตถุระดับสูงกว่าทึบแสงไปยัง gc หากไม่มีความรู้เกี่ยวกับหน่วยความจำภายใน gc จะไม่สามารถทำลายวัตถุได้ ซึ่งน่าจะเป็นข้อสรุปของคุณ
dbcb

4

รูปแบบตัวทำลายวัตถุเป็นพื้นฐานของการจัดการข้อผิดพลาดในการเขียนโปรแกรมระบบ แต่ไม่มีส่วนเกี่ยวข้องกับการรวบรวมขยะ ค่อนข้างจะเกี่ยวข้องกับอายุการใช้งานของวัตถุที่จับคู่กับขอบเขตและสามารถนำไปใช้ / ใช้ในภาษาใด ๆ ที่มีฟังก์ชั่นชั้นหนึ่ง

ตัวอย่าง (pseudocode) สมมติว่าคุณมีประเภท "ไฟล์ดิบ" เช่นประเภทตัวอธิบายไฟล์ Posix มีด้วยกันสี่พื้นฐานการดำเนินงานมีopen(), close(), ,read() write()คุณต้องการใช้งานไฟล์ประเภท "ปลอดภัย" ที่จะล้างออกเองเสมอ (นั่นคือมีตัวสร้างอัตโนมัติและตัวทำลายระบบ)

ฉันจะถือว่าภาษาของเรามีการจัดการข้อยกเว้นด้วยthrow, tryและfinally(ในภาษาโดยไม่ต้องจัดการคุณสามารถตั้งค่าวินัยที่ผู้ใช้ประเภทของคุณส่งกลับค่าพิเศษที่จะระบุข้อผิดพลาดข้อยกเว้น.)

คุณตั้งค่าฟังก์ชันที่ยอมรับฟังก์ชันที่ใช้งานได้ ฟังก์ชันผู้ปฏิบัติงานยอมรับหนึ่งอาร์กิวเมนต์ (ตัวจัดการกับไฟล์ "ปลอดภัย")

with_file_opened_for_read (string:   filename,
                           function: worker_function(safe_file f)):
  raw_file rf = open(filename, O_RDONLY)
  if rf == error:
    throw File_Open_Error

  try:
    worker_function(rf)
  finally:
    close(rf)

คุณยังให้การใช้งานread()และwrite()สำหรับsafe_file(ที่เพิ่งเรียกraw_file read()และwrite()) ตอนนี้ผู้ใช้ใช้safe_fileประเภทนี้:

...
with_file_opened_for_read ("myfile.txt",
                           anonymous_function(safe_file f):
                             mytext = read(f)
                             ... (including perhaps throwing an error)
                          )

destructor C ++ นั้นเป็นน้ำตาลเชิงซ้อนสำหรับtry-finallyบล็อก สิ่งที่ฉันทำที่นี่คือแปลงsafe_fileคลาสC ++ กับคอนสตรัคเตอร์และ destructor ที่จะคอมไพล์ โปรดทราบว่า C ++ ไม่มีfinallyข้อยกเว้นโดยเฉพาะเนื่องจาก Stroustrup รู้สึกว่าการใช้ destructor ที่ชัดเจนนั้นดีกว่า syntactically (และเขาแนะนำให้เป็นภาษาก่อนที่ภาษาจะมีฟังก์ชั่นที่ไม่ระบุชื่อ)

(นี่เป็นการลดความซับซ้อนของวิธีการหนึ่งที่ผู้คนใช้ในการจัดการข้อผิดพลาดในภาษาที่เหมือนเสียงกระเพื่อมมาหลายปีฉันคิดว่าฉันพบเจอครั้งแรกในช่วงปลายทศวรรษ 1980 หรือต้นปี 1990 แต่ฉันจำไม่ได้ว่าอยู่ที่ไหน)


สิ่งนี้อธิบายถึง internals ของรูปแบบ destructor แบบอิงสแต็กใน C ++ แต่ไม่ได้อธิบายว่าทำไมภาษาที่รวบรวมขยะไม่สามารถใช้ฟังก์ชันดังกล่าวได้ คุณอาจถูกต้องว่าสิ่งนี้ไม่มีส่วนเกี่ยวข้องกับการรวบรวมขยะ แต่เกี่ยวข้องกับการทำลาย / การสรุปวัตถุทั่วไปซึ่งดูเหมือนจะยากหรือไม่มีประสิทธิภาพในภาษาที่รวบรวมขยะ ดังนั้นหากการทำลายทั่วไปไม่ได้รับการสนับสนุน
dbcb

ดังที่ฉันได้กล่าวไว้ในตอนต้น: ภาษาใด ๆ ที่เก็บรวบรวมขยะที่มีฟังก์ชั่นชั้นหนึ่ง (หรือประมาณฟังก์ชั่นชั้นหนึ่ง) ให้ความสามารถในการจัดเตรียมอินเทอร์เฟซ "bullet Proof" เช่นsafe_fileและwith_file_opened_for_read(วัตถุที่ปิดตัวเองเมื่อ ) นั่นคือสิ่งสำคัญที่ไม่มีไวยากรณ์เหมือนกับตัวสร้างที่ไม่เกี่ยวข้อง Lisp, Scheme, Java, Scala, Go, Haskell, Rust, Javascript, Clojure ทั้งหมดรองรับฟังก์ชั่นชั้นหนึ่งที่เพียงพอดังนั้นพวกเขาจึงไม่ต้องการ destructors เพื่อให้คุณสมบัติที่มีประโยชน์เหมือนกัน
Wandering Logic

ฉันคิดว่าฉันเห็นสิ่งที่คุณพูด เนื่องจากภาษามีการบล็อกพื้นฐาน (ลอง / จับ / ในที่สุด, ฟังก์ชั่นชั้นหนึ่ง, ฯลฯ ) เพื่อใช้ฟังก์ชั่นที่เหมือน destructor ด้วยตนเองพวกเขาไม่ต้องการ destructors? ฉันเห็นบางภาษาใช้เส้นทางนั้นด้วยเหตุผลของความเรียบง่าย แม้ว่าดูเหมือนว่าไม่น่าจะเป็นเหตุผลหลักสำหรับภาษาทั้งหมดที่ระบุไว้ แต่อาจเป็นสิ่งที่มันเป็น บางทีฉันอาจเป็นแค่ชนกลุ่มน้อยผู้หลงรัก C ++ destructors และไม่มีใครสนใจจริงๆซึ่งอาจเป็นเหตุผลว่าทำไมภาษาส่วนใหญ่จึงไม่ใช้ destructors พวกเขาไม่สนใจ
dbcb

2

นี่ไม่ใช่คำตอบที่สมบูรณ์สำหรับคำถาม แต่ฉันต้องการเพิ่มการสังเกตสองสามข้อที่ไม่ได้กล่าวถึงในคำตอบหรือความคิดเห็นอื่น ๆ

  1. คำถามโดยปริยายถือว่าเรากำลังพูดถึงภาษาเชิงวัตถุสไตล์ Simula ซึ่ง จำกัด ตัวเองอยู่ ในภาษาส่วนใหญ่แม้แต่ผู้ที่มีวัตถุไม่ใช่ทุกอย่างเป็นวัตถุ เครื่องจักรที่จะติดตั้งอุปกรณ์ทำลายล้างจะกำหนดต้นทุนซึ่งผู้ดำเนินการด้านภาษาทุกคนเต็มใจจ่าย

  2. C ++ มีการรับประกันโดยนัยเกี่ยวกับคำสั่งทำลาย หากคุณมีโครงสร้างข้อมูลที่เหมือนต้นไม้ตัวอย่างเช่นเด็ก ๆ จะถูกทำลายก่อนที่ผู้ปกครอง นี่ไม่ใช่กรณีในภาษา GC'd ดังนั้นทรัพยากรลำดับชั้นอาจถูกนำออกใช้ในลำดับที่คาดเดาไม่ได้ สำหรับทรัพยากรที่ไม่ใช่หน่วยความจำสิ่งนี้อาจสำคัญ


2

เมื่อทั้งสองกรอบของ GC ที่เป็นที่นิยมที่สุด (Java และ. NET) ได้รับการออกแบบฉันคิดว่าผู้เขียนคาดว่าการสรุปจะทำงานได้ดีพอที่จะหลีกเลี่ยงความจำเป็นในการจัดการทรัพยากรในรูปแบบอื่น การออกแบบภาษาและกรอบงานหลายด้านสามารถทำให้ง่ายขึ้นอย่างมากหากไม่จำเป็นต้องใช้คุณลักษณะทั้งหมดที่จำเป็นเพื่อรองรับการจัดการทรัพยากรที่เชื่อถือได้และกำหนดขึ้น 100% ใน C ++ จำเป็นต้องแยกความแตกต่างระหว่างแนวคิดของ:

  1. ตัวชี้ / การอ้างอิงที่ระบุวัตถุซึ่งเป็นเจ้าของโดยผู้ถือการอ้างอิงเท่านั้นและไม่ได้รับการระบุโดยตัวชี้ / การอ้างอิงใด ๆ ที่เจ้าของไม่ทราบ

  2. ตัวชี้ / การอ้างอิงที่ระบุวัตถุที่สามารถแบ่งใช้ได้ซึ่งไม่ได้เป็นของใครโดยเฉพาะ

  3. ตัวชี้ / การอ้างอิงที่ระบุวัตถุที่เป็นเจ้าของโดยผู้อ้างอิงเท่านั้น แต่สามารถเข้าถึงได้ผ่าน "มุมมอง" เจ้าของไม่มีวิธีการติดตาม

  4. ตัวชี้ / การอ้างอิงที่ระบุวัตถุซึ่งให้มุมมองของวัตถุที่เป็นของคนอื่น

หาก GC / กรอบภาษาไม่ต้องกังวลเกี่ยวกับการจัดการทรัพยากรทั้งหมดข้างต้นสามารถแทนที่ด้วยการอ้างอิงชนิดเดียว

ฉันจะพบว่าความคิดที่ว่าการปิดท้ายที่สุดจะช่วยขจัดความจำเป็นในการจัดการทรัพยากรในรูปแบบอื่น ๆ แต่ความคาดหวังดังกล่าวนั้นสมเหตุสมผลหรือไม่ในขณะนั้นประวัติศาสตร์ได้แสดงให้เห็นว่ามีหลายกรณีที่ต้องการการจัดการทรัพยากรที่แม่นยำ . ฉันคิดว่ารางวัลของการจดจำความเป็นเจ้าของในระดับภาษา / เฟรมเวิร์กจะเพียงพอที่จะพิสูจน์ค่าใช้จ่าย (ความซับซ้อนต้องมีอยู่ที่ใดที่หนึ่งและการย้ายไปที่ภาษา / เฟรมเวิร์กจะทำให้รหัสผู้ใช้ง่ายขึ้น) แต่จำได้ว่า การออกแบบมีประโยชน์ในการมีการอ้างอิง "ชนิด" เดียว - สิ่งที่ใช้งานได้เฉพาะในกรณีที่ภาษา / กรอบงานไม่สามารถใช้ได้กับปัญหาของการล้างทรัพยากร


2

เหตุใดกระบวนทัศน์ของผู้ทำลายล้างวัตถุในภาษาที่รวบรวมขยะจึงหายไปอย่างแพร่หลาย?

ฉันมาจากพื้นหลัง C ++ ดังนั้นพื้นที่นี้ทำให้ฉันงุนงง

destructor ใน C ++ จริง ๆ แล้วรวมสอง สิ่งเข้าด้วยกัน มันเพิ่มแรมและมันช่วยเพิ่มรหัสทรัพยากร

ภาษาอื่น ๆแยกข้อกังวลเหล่านี้โดยให้ GC รับผิดชอบการเพิ่ม RAM ในขณะที่คุณสมบัติภาษาอื่นรับผิดชอบการเพิ่มรหัสทรัพยากร

ฉันพบว่าภาษาเหล่านี้ไม่ได้พิจารณาถึงความทรงจำเป็นอย่างยิ่งเพราะเป็นเพียงการจัดการทรัพยากรที่คุ้มค่า

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

เกี่ยวกับซ็อกเก็ตตัวจัดการไฟล์สถานะแอปพลิเคชัน

ภาษาสามารถให้วิธีต่างๆในการเพิ่มรหัสทรัพยากรโดย:

  • คู่มือ.CloseOrDispose()กระจายอยู่ทั่วรหัส

  • คู่มือ.CloseOrDispose()กระจายอยู่ภายใน " finallyบล็อก" ด้วยตนเอง

  • คู่มือ "ทรัพยากรบล็อก id" (เช่นusing, with, tryเมื่อใช้ทรัพยากรฯลฯ ) ซึ่งโดยอัตโนมัติ.CloseOrDispose()หลังจากที่บล็อกจะทำ

  • รับประกัน "resource id blocks" ซึ่งจะดำเนินการโดยอัตโนมัติ.CloseOrDispose()หลังจากบล็อกเสร็จสิ้น

หลายภาษาใช้กลไกแบบแมนนวล (เมื่อเทียบกับการรับประกัน) ซึ่งสร้างโอกาสสำหรับการจัดการทรัพยากร ใช้รหัส NodeJS ง่าย ๆ นี้:

require('fs').openSync('file1.txt', 'w');
// forget to .closeSync the opened file

.. ที่โปรแกรมเมอร์ได้ลืมปิดไฟล์ที่เปิดอยู่

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

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

การปล่อยรหัสทรัพยากรภายในตัวทำลาย C ++ นั้นไม่รับประกันเช่นกัน คุณอาจคิดว่า RAII ดำเนินการเช่นการรับประกัน "ทรัพยากรบล็อก id" ยังแตกต่างจาก "ทรัพยากรบล็อก id" ภาษา C ++ ไม่หยุดวัตถุให้บล็อก RAII จากการรั่วไหลออกมาเพื่อให้บล็อก RAII อาจไม่ได้รับการทำ


ดูเหมือนว่าภาษาที่รวบรวมขยะสมัยใหม่เกือบทั้งหมดที่มีการสนับสนุนวัตถุ OOPy เช่น Ruby, Javascript / ES6 / ES7, Actionscript, Lua และอื่น ๆ ละเว้นการ destructor / จบกระบวนทัศน์อย่างสมบูรณ์ Python ดูเหมือนจะเป็นคนเดียวที่มี__del__()วิธีการเรียน ทำไมนี้

เพราะพวกเขาจัดการรหัสทรัพยากรโดยใช้วิธีอื่นตามที่กล่าวไว้ข้างต้น

การตัดสินใจออกแบบภาษาที่นำไปสู่ภาษาเหล่านี้ไม่มีวิธีใดในการดำเนินการตรรกะที่กำหนดเองในการกำจัดวัตถุคืออะไร?

เพราะพวกเขาจัดการรหัสทรัพยากรโดยใช้วิธีอื่นตามที่กล่าวไว้ข้างต้น

ทำไมภาษาถึงมีแนวคิดในตัวของอินสแตนซ์ของวัตถุที่มีโครงสร้างคลาสหรือคล้ายคลาสพร้อมกับการสร้างอินสแตนซ์ที่กำหนดเอง (constructors) แต่ยังละเว้นฟังก์ชันการทำลาย / การทำให้เสร็จสมบูรณ์อย่างสมบูรณ์?

เพราะพวกเขาจัดการรหัสทรัพยากรโดยใช้วิธีอื่นตามที่กล่าวไว้ข้างต้น

ฉันเห็นข้อโต้แย้งที่เป็นไปได้ว่า destructor / finalizer อาจไม่ได้รับการเรียกจนกว่าจะถึงเวลาที่แน่นอนในอนาคต แต่นั่นก็ไม่ได้หยุด Java หรือ Python จากการสนับสนุนการทำงาน

Java ไม่มี destructors

เอกสาร Java พูดถึง :

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

.. แต่การวางรหัสการจัดการรหัสทรัพยากรภายในObject.finalizerนั้นส่วนใหญ่ถือว่าเป็นรูปแบบการต่อต้าน ( cf. ) ควรเขียนรหัสเหล่านั้นที่ไซต์การโทร

สำหรับผู้ที่ใช้รูปแบบการต่อต้านเหตุผลของพวกเขาคือพวกเขาอาจลืมที่จะปล่อยรหัสทรัพยากรที่ไซต์การโทร ดังนั้นพวกเขาทำมันอีกครั้งในรอบสุดท้ายในกรณี

อะไรคือเหตุผลหลักในการออกแบบภาษาที่ไม่รองรับรูปแบบของการสรุปวัตถุ

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

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

finalize() {
    Log(TimeNow() + ". Obj " + toString() + " is going to be memory-collected soon!"); // "soon"
}

-1

พบการอ้างอิงเกี่ยวกับเรื่องนี้ใน Dr Dobbs wrt c ++ ที่มีความคิดทั่วไปที่ระบุว่า destructors เป็นปัญหาในภาษาที่นำไปใช้ ความคิดคร่าวๆที่นี่ดูเหมือนว่าเป็นจุดประสงค์หลักของ destructors คือการจัดการการจัดสรรคืนหน่วยความจำและยากที่จะทำอย่างถูกต้อง หน่วยความจำถูกจัดสรรเป็นชิ้น ๆ แต่วัตถุต่าง ๆ เชื่อมต่อกันแล้วความรับผิดชอบในการจัดสรรคืน / ขอบเขตไม่ชัดเจนนัก

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

กล่าวอีกนัยหนึ่งความคิดของมนุษย์ที่เป็น "วัตถุที่ไม่ได้ใช้งานใหม่" เป็นจริงในบางวิธีที่เป็นนามธรรมที่ทำให้เข้าใจผิดในแง่ที่ว่าวัตถุไม่สามารถ "ทันที" กลายเป็นไม่ได้ใช้ วัตถุที่ไม่ได้ใช้สามารถ "ค้นพบ" ได้โดยการเรียกใช้อัลกอริธึมการรวบรวมขยะที่สำรวจกราฟอ้างอิงวัตถุ

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

วิธีแก้ปัญหาด้านการจัดการทรัพยากรเช่นไฟล์หรือการเชื่อมต่อดูเหมือนจะมีวัตถุ "ผู้จัดการ" ที่พยายามจัดการการใช้งานของพวกเขา


2
ค้นหาที่น่าสนใจ ขอบคุณ อาร์กิวเมนต์ของผู้เขียนนั้นขึ้นอยู่กับ destructor ที่ถูกเรียกในเวลาที่ไม่ถูกต้องเนื่องจากผ่านคลาสอินสแตนซ์ตามค่าที่คลาสไม่มีตัวสร้างการคัดลอกที่เหมาะสม (ซึ่งเป็นปัญหาจริง) อย่างไรก็ตามสถานการณ์นี้ไม่ได้มีอยู่จริงในภาษาไดนามิกส่วนใหญ่ (ถ้าไม่ใช่ทั้งหมด) เพราะทุกอย่างผ่านการอ้างอิงซึ่งหลีกเลี่ยงสถานการณ์ของผู้เขียน แม้ว่านี่จะเป็นมุมมองที่น่าสนใจ แต่ฉันไม่คิดว่าจะอธิบายว่าทำไมภาษาที่รวบรวมขยะส่วนใหญ่เลือกที่จะละเว้นฟังก์ชันการทำงานของ
dbcb

2
คำตอบนี้แสดงบทความของดร. ดอบบ์ผิดบทความไม่ได้โต้แย้งว่าผู้ทำลายระบบเป็นปัญหาโดยทั่วไป บทความระบุว่าสิ่งนี้จริง: การจัดการหน่วยความจำแบบดั้งเดิมเป็นเหมือนคำสั่ง goto เพราะทั้งง่ายและมีประสิทธิภาพเกินไป ในลักษณะเดียวกับที่คำสั่ง goto ได้รับการห่อหุ้มที่ดีที่สุดใน "โครงสร้างการควบคุมที่ จำกัด อย่างเหมาะสม" (ดู: Dijktsra) วิธีการจัดการหน่วยความจำแบบดั้งเดิมนั้นมีการห่อหุ้มที่ดีที่สุดใน "โครงสร้างข้อมูลที่ จำกัด อย่างเหมาะสม" Destructors เป็นขั้นตอนในทิศทางนี้ แต่ไม่ไกลพอ ตัดสินใจเองว่าเป็นเรื่องจริงหรือไม่
kdbanman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.