ทำไมคอมไพเลอร์ไม่ใส่ deallocations โดยอัตโนมัติ?


63

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

แก้ไข: สำหรับการอ้างอิงในอนาคตนี่คือการสนทนาอื่นที่มีตัวอย่างที่น่าสนใจ


125
และนั่นคือเหตุผลว่าทำไมเราจึงสอนทฤษฎีการคำนวณให้คุณ ;)
Raphael

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

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

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

2
@ BorisTreukhov โปรดนำไปที่ห้องแชท ไม่ฉันไม่คิดว่า Andrej กำลังพูดว่าการวิเคราะห์ทางหนีภัยนั้นเป็นไปไม่ได้ การวิเคราะห์การหลบหนีที่แม่นยำอย่างสมบูรณ์แบบนั้นไม่สามารถบอกได้ ทุกกรุณานำไปสนทนา โปรดโพสต์ความคิดเห็นที่นี่ซึ่งมีวัตถุประสงค์เพื่อปรับปรุงคำถาม - การอภิปรายและความเห็นอื่น ๆ ควรโพสต์ในห้องสนทนา
DW

คำตอบ:


80

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


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

1
ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Gilles

2
พวกเอาไปคุยกัน ทุกสิ่งที่ไม่เกี่ยวข้องโดยตรงกับคำตอบของตัวเองและวิธีการปรับปรุงสามารถลบออกได้
กราฟิลส์

2
สิ่งที่คอมไพเลอร์ทำกันอย่างมีความสุขนั้นไม่สามารถตัดสินใจได้โดยทั่วไป เราจะไม่ไปไหนในโลกของคอมไพเลอร์ถ้าเราทำตามทฤษฎีบทของไรซ์เสมอ
Tikhon Jelvis

3
นี่ไม่เกี่ยวข้อง หากมันไม่สามารถอธิบายได้สำหรับคอมไพเลอร์ทั้งหมดมันก็ไม่สามารถอธิบายได้สำหรับมนุษย์ทุกคนเช่นกัน แต่เราคาดหวังให้มนุษย์ใส่free()อย่างถูกต้อง
พอลเดรเปอร์

53

ดังที่ David Richerby กล่าวไว้อย่างถูกต้องปัญหาดังกล่าวไม่สามารถระบุได้โดยทั่วไป Object liveness เป็นคุณสมบัติระดับโลกของโปรแกรมและโดยทั่วไปอาจขึ้นอยู่กับอินพุตของโปรแกรม

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

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

การติดตั้งใช้งานบนพื้นฐานของการนับการอ้างอิงนั้นใกล้เคียงกับ "การรวบรวมการจัดสรร deallocations" ซึ่งยากที่จะบอกความแตกต่าง ตัวอย่างการอ้างอิงอัตโนมัติของLLVM (ใช้ในObjective-CและSwift ) เป็นตัวอย่างที่มีชื่อเสียง

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

ตอนนี้ในหัวข้อของมนุษย์มีสามวิธีหลักที่มนุษย์จัดการอายุการใช้งานการจัดสรรด้วยตนเอง:

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

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

2
นี่คือคำตอบที่ดีที่สุดสำหรับคำถาม (ซึ่งคำตอบมากเกินไปไม่ได้อยู่) คุณอาจจะได้เพิ่มการอ้างอิงถึงการสำรวจการทำงานของฮันส์ Boehm ในGC conseervative : en.wikipedia.org/wiki/Boehm_garbage_collector อีกจุดที่น่าสนใจก็คือข้อมูล (หรือประโยชน์ในการขยายความรู้สึก) ข้อมูลสามารถกำหนดด้วยความเคารพในความหมายของนามธรรมหรือรูปแบบการดำเนินการ แต่หัวข้อนั้นกว้างจริงๆ
Babou

29

มันเป็นปัญหาที่ไม่สมบูรณ์ไม่ใช่ปัญหาที่แก้ไม่ได้

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

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

ตัวอย่างเช่นสมมติว่าคุณกำลังเขียนRead-Evaluate-Print-Loop (REPL) : ประเภทผู้ใช้ในคำสั่งและโปรแกรมของคุณดำเนินการ ผู้ใช้สามารถจัดสรร / ยกเลิกการจัดสรรหน่วยความจำโดยพิมพ์คำสั่งลงใน REPL ของคุณ ซอร์สโค้ดของคุณจะระบุว่า REPL ควรทำอย่างไรกับคำสั่งผู้ใช้ที่เป็นไปได้แต่ละคำสั่งรวมถึงการยกเลิกการจัดสรรเมื่อผู้ใช้พิมพ์คำสั่งนั้น

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

tl; dr ปัญหาคือซอร์สโค้ด C ไม่ได้ให้คอมไพเลอร์ที่มีความรู้ภายนอก ความเด็ดเดี่ยวไม่ได้เป็นปัญหาเพราะมีอยู่ไม่ว่าจะเป็นกระบวนการด้วยตนเองหรือโดยอัตโนมัติ


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

23

ขณะนี้ไม่มีคำตอบที่โพสต์ถูกต้องครบถ้วน

ทำไมคอมไพเลอร์ไม่ใส่ deallocations โดยอัตโนมัติ?

  1. บางคนทำ (ฉันจะอธิบายในภายหลัง)

  2. คุณสามารถโทรหาfree()ก่อนที่โปรแกรมจะออก แต่คำถามของคุณต้องการการโทรfree()โดยเร็วที่สุดเท่าที่จะเป็นไปได้

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

  4. ปัญหา undecidable ไม่สามารถแก้ไขได้ในเวลาที่ จำกัด โดยอัลกอริทึมใด ๆ ไม่ว่าจะเป็นคอมไพเลอร์หรือมนุษย์

  5. มนุษย์ (ลอง) เขียนในส่วนย่อยของโปรแกรม C ที่สามารถตรวจสอบความถูกต้องของหน่วยความจำได้โดยอัลกอริธึม (ตัวเอง)

  6. บางภาษาบรรลุ # 1 โดยการสร้าง # 5 ลงในคอมไพเลอร์ พวกเขาไม่อนุญาตให้โปรแกรมที่ใช้การจัดสรรหน่วยความจำโดยพลการ แต่เป็นเซตย่อยที่ตัดสินใจได้ของพวกเขา FothและRustเป็นสองตัวอย่างของภาษาที่มีการจัดสรรหน่วยความจำที่ จำกัด มากกว่าของ C malloc()ที่สามารถ (1) ตรวจพบว่าโปรแกรมถูกเขียนนอกชุดที่สามารถตัดสินใจได้ (2) แทรกการจัดสรรคืนอัตโนมัติ


1
ฉันเข้าใจว่าสนิมทำอย่างไร แต่ฉันไม่เคยได้ยินเกี่ยวกับ Forth ที่ทำสิ่งนี้ คุณสามารถทำอย่างละเอียด?
Milton Silva

2
@MiltonSilva, Forth - อย่างน้อยที่สุดก็เป็นพื้นฐานการดำเนินงานดั้งเดิม - มีเพียงกองไม่ได้เป็นกอง มันทำให้การจัดสรร / การจัดสรรคืนย้ายตัวชี้การโทรสแต็คงานที่คอมไพเลอร์สามารถทำได้อย่างง่ายดาย Forth ถูกสร้างขึ้นมาเพื่อกำหนดเป้าหมายฮาร์ดแวร์ที่ง่ายมากและบางครั้งหน่วยความจำที่ไม่ได้ทำงานแบบไดนามิกนั้นเป็นสิ่งที่สามารถใช้งานได้ เห็นได้ชัดว่าไม่ใช่วิธีแก้ปัญหาที่ใช้การได้สำหรับโปรแกรมที่ไม่สำคัญ
พอลเดรเปอร์

10

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

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


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

1. ฉันไม่เคยพูดว่าเครื่องคิดอาจผิดพลาดได้ ดีกว่ามนุษย์คือสิ่งที่พวกเขามีอยู่แล้วในหลายกรณี 2. ความคาดหวังของความสมบูรณ์แบบ (ที่มีศักยภาพ) เป็นสิ่งที่จำเป็นสำหรับการกระทำคือความไร้สาระ
André Souza Lemos

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

เจตนาในมนุษย์เช่นเดียวกับในเครื่องเสมอมาจากข้างนอก
André Souza Lemos

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

9

การขาดการจัดการหน่วยความจำอัตโนมัติเป็นคุณสมบัติของภาษา

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


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
DW

2
นี่เป็นคำตอบของคำถาม (ส่วน CS) อย่างไร?
Raphael

6
@ ราฟาเอลวิทยาศาสตร์คอมพิวเตอร์ไม่ได้หมายความว่าเราควรมองหาคำตอบทางเทคนิคที่คลุมเครือ คอมไพเลอร์ทำหลายสิ่งที่เป็นไปไม่ได้ในกรณีทั่วไป หากเราต้องการการจัดการหน่วยความจำอัตโนมัติเราสามารถนำไปใช้ได้หลายวิธี C ไม่ทำเช่นนั้นเพราะมันไม่ควรทำ
Jouni Sirén

9

ปัญหาส่วนใหญ่เป็นสิ่งประดิษฐ์ทางประวัติศาสตร์ไม่ใช่สิ่งที่เป็นไปไม่ได้ของการนำไปใช้

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

หากคุณมองไปที่ภาษาระดับต่ำที่ทันสมัยกว่าเช่น C ++ 11 หรือ Rust คุณจะพบว่าพวกเขาส่วนใหญ่แก้ไขปัญหาด้วยการทำให้ความเป็นเจ้าของหน่วยความจำชัดเจนในประเภทของตัวชี้ ใน C ++ คุณจะใช้unique_ptr<T>แทนธรรมดาT*ที่จะถือหน่วยความจำและทำให้แน่ใจว่าหน่วยความจำที่ได้รับการปลดปล่อยเมื่อวัตถุถึงจุดสิ้นสุดของขอบเขตซึ่งแตกต่างจากธรรมดาunique_ptr<T> T*โปรแกรมเมอร์สามารถส่งหน่วยความจำจากที่หนึ่งunique_ptr<T>ไปยังอีกที่หนึ่ง แต่จะมีเพียงคนเดียวที่unique_ptr<T>ชี้ไปที่หน่วยความจำ ดังนั้นจึงเป็นที่ชัดเจนเสมอว่าใครเป็นเจ้าของหน่วยความจำและเมื่อใดที่ต้องมีการปลดปล่อย

C ++ unique_ptr<T>สำหรับเหตุผลที่เข้ากันได้ย้อนหลังยังคงรูปแบบเดิมช่วยให้จัดการหน่วยความจำคู่มือและทำให้การสร้างข้อบกพร่องหรือวิธีการที่จะหลีกเลี่ยงการคุ้มครองที่ สนิมยิ่งเข้มงวดในการบังคับใช้กฎการเป็นเจ้าของหน่วยความจำผ่านข้อผิดพลาดของคอมไพเลอร์

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


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
ราฟาเอล

6

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

ประเด็นหนึ่งที่ยังไม่ได้กล่าวถึงคือความล่าช้าในการเก็บขยะ ใน C เมื่อโปรแกรมเมอร์โทรฟรี () หน่วยความจำนั้นจะพร้อมใช้งานทันที (ในทางทฤษฎีอย่างน้อยที่สุด!) ดังนั้นโปรแกรมเมอร์จึงสามารถเพิ่มโครงสร้าง 100MB ของพวกเขาได้, จัดสรรโครงสร้างอีก 100MB เป็นมิลลิวินาทีในภายหลังและคาดว่าการใช้หน่วยความจำโดยรวมจะยังคงเหมือนเดิม

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

หากคุณใช้งานบนพีซีที่มีกิ๊ก RAM และหน่วยความจำเสมือนแน่นอนว่าคุณอาจไม่เคยเห็นสิ่งนี้เลย หากคุณกำลังทำงานบนระบบที่มีทรัพยากร จำกัด มากขึ้น (เช่นระบบฝังตัวหรือโทรศัพท์) นี่คือสิ่งที่คุณต้องใช้อย่างจริงจัง นี่ไม่ใช่แค่เชิงทฤษฎี - ฉันเห็นเป็นการส่วนตัวแล้วว่าสร้างปัญหา (เช่นเดียวกับการกระแทกอุปกรณ์ประเภทของปัญหา) เมื่อทำงานกับระบบ WinCE โดยใช้. NET Compact Framework และพัฒนาใน C #


ในทางทฤษฎีคุณสามารถรัน GC ก่อนการจัดสรรทุกครั้ง
adrianN

4
@adrianN แต่ในทางปฏิบัติมันไม่ได้ทำเพราะมันจะเป็นจิต ประเด็นของเกรแฮมยังคงมีอยู่: GCs มักจะมีค่าใช้จ่ายจำนวนมากไม่ว่าจะเป็นรันไทม์หรือในแง่ของหน่วยความจำส่วนเกินที่จำเป็น คุณสามารถปรับสมดุลนี้ไปทางสุดขั้ว แต่คุณไม่สามารถลบค่าใช้จ่ายได้
Konrad Rudolph

"การหน่วงเวลา" เมื่อหน่วยความจำว่างเพิ่มขึ้นเป็นปัญหาในระบบหน่วยความจำเสมือนมากกว่าในระบบที่มีทรัพยากร จำกัด ในกรณีก่อนหน้าอาจเป็นการดีกว่าที่โปรแกรมจะใช้ 100MB มากกว่า 200MB แม้ว่าระบบจะมีให้ใช้ 200MBแต่ในกรณีหลังจะไม่มีประโยชน์ในการใช้งาน GC เร็วกว่าที่จำเป็นเว้นแต่ความล่าช้าจะเป็นที่ยอมรับมากกว่าในระหว่าง ส่วนของรหัสกว่าในช่วงอื่น ๆ
supercat

ฉันไม่เห็นว่าสิ่งนี้จะพยายามตอบคำถาม (ส่วน CS) อย่างไร
Raphael

1
@ ราฟาเอลฉันได้อธิบายปัญหาที่ได้รับการยอมรับอย่างดีเกี่ยวกับหลักการของการเก็บขยะซึ่งสอน (หรือควรจะ) สอนใน CS เป็นหนึ่งในข้อเสียพื้นฐาน ฉันยังได้รับประสบการณ์ส่วนตัวของฉันที่ได้เห็นสิ่งนี้ในทางปฏิบัติเพื่อแสดงว่ามันไม่ใช่ปัญหาเชิงทฤษฎีอย่างแท้จริง หากคุณไม่เข้าใจบางสิ่งเกี่ยวกับเรื่องนี้ฉันยินดีที่จะพูดคุยกับคุณเพื่อพัฒนาความรู้ในเรื่องนี้
เกรแฮม

4

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

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

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


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

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

4

สิ่งที่จะทำ: มีการเก็บขยะเป็นและมีคอมไพเลอร์ใช้นับการอ้างอิง (Objective-C, Swift) ผู้ที่นับการอ้างอิงต้องการความช่วยเหลือจากโปรแกรมเมอร์โดยหลีกเลี่ยงรอบการอ้างอิงที่รัดกุม

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

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


ฉันไม่เห็นผลงานที่นี่
Babou

3

ในภาษาเช่น C โปรแกรมเมอร์คาดว่าจะแทรกการโทรฟรี ทำไมคอมไพเลอร์ไม่ทำสิ่งนี้โดยอัตโนมัติ

เนื่องจากอายุการใช้งานของบล็อกหน่วยความจำคือการตัดสินใจของโปรแกรมเมอร์ไม่ใช่ของคอมไพเลอร์

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

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


-1

ในภาษาเช่น C โปรแกรมเมอร์คาดว่าจะแทรกการโทรฟรี ทำไมคอมไพเลอร์ไม่ทำสิ่งนี้โดยอัตโนมัติ

ใน C และภาษาอื่น ๆ มีสิ่งอำนวยความสะดวกที่จะทำให้คอมไพเลอร์ทำสิ่งนี้ในกรณีที่มันชัดเจนในเวลารวบรวมเมื่อมันควรจะทำ: การใช้ตัวแปรระยะเวลาอัตโนมัติ (เช่นตัวแปรท้องถิ่นทั่วไป) . คอมไพเลอร์มีหน้าที่รับผิดชอบในการจัดการพื้นที่ว่างให้เพียงพอสำหรับตัวแปรดังกล่าวและสำหรับการปล่อยพื้นที่นั้นเมื่ออายุการใช้งาน (กำหนดไว้อย่างดี) สิ้นสุดลง

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

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

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