ข้อเสียของการจัดการหน่วยความจำตามขอบเขต


38

ฉันชอบการจัดการหน่วยความจำตามขอบเขต (SBMM) หรือRAIIจริงๆแล้วเนื่องจากชุมชน C ++ เป็นที่รู้จักกันมากกว่าปกติ เท่าที่ฉันรู้ยกเว้น C ++ (และ C) ไม่มีภาษากระแสหลักอื่น ๆ ที่ใช้อยู่ในปัจจุบันซึ่งทำให้ SBMM / RAII เป็นกลไกการจัดการหน่วยความจำหลักและแทนที่จะต้องการใช้การรวบรวมขยะ (GC)

ฉันพบว่ามันค่อนข้างสับสนตั้งแต่

  1. SBMM ทำให้โปรแกรมกำหนดค่าได้มากขึ้น (คุณสามารถบอกได้อย่างแม่นยำเมื่อวัตถุถูกทำลาย)
  2. ในภาษาที่ใช้ GC คุณมักจะต้องทำการจัดการทรัพยากรด้วยตนเอง (ดูที่การปิดไฟล์ใน Java เช่น) ซึ่งบางส่วนเอาชนะวัตถุประสงค์ของ GC และยังมีข้อผิดพลาดเกิดขึ้นได้ง่าย
  3. หน่วยความจำฮีปสามารถ (ขอบเขตอย่างสวยงามมาก, imo) ได้เช่นกัน (ดูstd::shared_ptrใน C ++)

ทำไม SBMM ถึงไม่ใช้กันอย่างแพร่หลาย? ข้อเสียของมันคืออะไร?


1
ข้อเสียบางส่วน (โดยเฉพาะที่เกี่ยวข้องกับความเร็ว) ถูกกล่าวถึงใน Wikipedia: en.wikipedia.org/wiki/ …
Philipp

2
ปัญหาการจัดการทรัพยากรด้วยตนเองของ Java เป็นผลข้างเคียงของการไม่รับประกันว่าfinalize()จะมีการเรียกวิธีการของวัตถุก่อนการรวบรวมขยะ ซึ่งจะสร้างปัญหาในระดับเดียวกับที่การรวบรวมขยะควรจะแก้ไข
Blrfl

7
@ Blrfl เรื่องไร้สาระ การจัดการทรัพยากร "ด้วยตนเอง" (สำหรับทรัพยากรอื่นที่ไม่ใช่หน่วยความจำอย่างชัดแจ้ง) จะดีกว่าแม้ว่าจะไม่มี "ปัญหา" เนื่องจาก GC สามารถทำงานได้นานมากหลังจากที่ทรัพยากรไม่ได้ใช้งานหรือแม้กระทั่งไม่ได้ทำงานเลย นั่นไม่ใช่ปัญหาสำหรับหน่วยความจำและการจัดการหน่วยความจำคือสิ่งที่คอลเลกชันขยะควรจะแก้ไข

4
BTW ฉันชอบที่จะอ้างถึงมันเป็น SBRM เนื่องจากคุณสามารถใช้กลไกเดียวกันเพื่อจัดการทรัพยากรโดยทั่วไปไม่ใช่แค่หน่วยความจำ
PlasmaHH

คำตอบ:


27

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

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

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

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

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

ปัจจุบันจุดที่สองของคุณคือการจัดการทรัพยากรแบบแมนนวลได้รับการแก้ไขผ่านข้อความที่ดำเนินการล้างข้อมูลตามขอบเขต แต่ไม่ได้ทำการล้างข้อมูลให้สอดคล้องกับเวลาชีวิตของวัตถุ (ดังนั้นการไม่โต้ตอบกับ GC และหน่วยความจำปลอดภัย) นี่คือusingC #, withใน Python, try-with-resources ใน Java เวอร์ชันล่าสุด


1
นี่ไม่ได้อธิบายว่าทำไมรูปแบบ nondeterministic เช่น GC ควรเหนือกว่ารูปแบบที่กำหนดขึ้นมา usingคำสั่งเป็นไปได้เฉพาะในประเทศ เป็นไปไม่ได้ที่จะล้างทรัพยากรที่มีอยู่ในตัวแปรสมาชิกด้วยวิธีนี้
Philipp

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

16
usingเป็นเรื่องตลกเมื่อเทียบกับ RAII เพียงเพื่อให้คุณรู้ว่า
DeadMG

3
@Philipp โปรดอธิบายการวัดของคุณสำหรับ "superior" มันเป็นความจริงที่การจัดการหน่วยความจำด้วยตนเองนั้นเร็วขึ้นในเวลาทำงานสำหรับจัดการกับการจัดการหน่วยความจำ อย่างไรก็ตามค่าใช้จ่ายของซอฟต์แวร์ไม่สามารถตัดสินได้อย่างหมดจดจากเวลา CPU ที่ใช้ในการจัดการหน่วยความจำเพียงอย่างเดียว
ArTs

2
@ArtT: ฉันไม่จำเป็นต้องเห็นด้วยกับสิ่งนี้ RAII มาพร้อมกับความต้องการที่วัตถุจะต้องถูกทำลายเมื่อมันออกจากขอบเขต ดังนั้นจึงต้องวนซ้ำเพื่อทำลายวัตถุ ภายใต้ GC generational สมัยใหม่การทำลายล้างเหล่านี้อาจถูกเลื่อนออกไปจนกว่าจะสิ้นสุดของลูปหรือแม้กระทั่งในภายหลัง GC สามารถทำได้เร็วมากในกรณีที่ดี
Phoshi

14

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

ภาษาที่รวบรวมขยะไม่สามารถใช้ RAII โดยตรง แต่มักจะเสนอไวยากรณ์ที่มีผลเทียบเท่า ใน Java เรามีคำสั่ง try-with-ressource

try (BufferedReader br = new BufferedReader(new FileReader(path))) { ... }

ซึ่งเรียก.close()ใช้ทรัพยากรโดยอัตโนมัติเมื่อออกจากบล็อก C # มีIDisposableอินเตอร์เฟสซึ่งอนุญาตให้.Dispose()เรียกเมื่อออกจากusing (...) { ... }คำสั่ง Python มีwithคำสั่ง:

with open(filename) as f:
    ...

ซึ่งทำงานในรูปแบบที่คล้ายกัน วิธีการเปิดไฟล์ของ Ruby ได้รับการเรียกกลับ หลังจากดำเนินการติดต่อกลับแล้วไฟล์จะถูกปิด

File.open(name, mode) do |f|
    ...
end

ฉันคิดว่า Node.js ใช้กลยุทธ์เดียวกัน


4
การใช้ฟังก์ชั่นการสั่งซื้อที่สูงขึ้นสำหรับการจัดการทรัพยากรย้อนกลับไปนานก่อนทับทิม ใน Lisps มันเป็นเรื่องธรรมดาที่จะมีพูดwith-open-filehandleฟังก์ชั่นที่เปิดไฟล์ให้มันฟังก์ชั่นและเมื่อกลับมาของฟังก์ชั่นปิดไฟล์อีกครั้ง
Jörg W Mittag

4
อาร์กิวเมนต์อ้างอิงแบบวนรอบค่อนข้างธรรมดา แต่มันสำคัญอย่างไรจริงๆ การอ้างอิงแบบวนรอบสามารถลดลงได้ด้วยการใช้พอยน์เตอร์พอยน์เตอร์หากความเป็นเจ้าของชัดเจน
Philipp

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

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

1
เหตุผลสำคัญที่ทำไมใช้การนับการอ้างอิงบ่อยครั้งก็คือมันช้ากว่า GC แม้ว่าจะมีความเรียบง่าย
Rufflewind

14

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

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

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


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

@supercat: "ความถูกต้อง GC มักต้องการการจัดการทรัพยากรที่มีขอบเขต" ใช่มั้ย? ขอบเขตนั้นมีอยู่ในซอร์สโค้ดและ GC เท่านั้นที่มีอยู่ในรันไทม์ (และดังนั้นจึงไม่สนใจการมีอยู่ของขอบเขต)
Jon Harrop

@ JonHarrop: ฉันใช้คำว่า "scope" ในความหมายเดียวกับ C ++ "scoped pointer" [อายุการใช้งานของวัตถุควรเป็นของ container ที่เก็บไว้] เนื่องจากเป็นการใช้งานโดยนัยจากคำถามเดิม ประเด็นของฉันคือวัตถุสร้างการอ้างอิงที่อาจเกิดขึ้นกับตัวเองเพื่อจุดประสงค์เช่นการรับเหตุการณ์อาจไม่สามารถนำมาประกอบกันได้ในระบบ GC ล้วนๆ เพื่อความถูกต้องการอ้างอิงบางอย่างจะต้องแข็งแรงและการอ้างอิงบางอย่างจะต้องอ่อนแอและการอ้างอิงใดที่จะต้องขึ้นอยู่กับวิธีการใช้วัตถุ ตัวอย่างเช่น ...
supercat

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

@supercat: ใช่ ฉันจะไม่พูดแบบนั้นบ่อยครั้ง ฉันเพิ่งเจอมันครั้งเดียวในรอบ 30 ปีของการเขียนโปรแกรม
Jon Harrop

7

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

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


9
ฉันไม่คิดว่าการปิดใน C ++เป็นตัวอย่างที่ดี lambdas ใน C ++ 11 เป็นเพียงน้ำตาล syntactic สำหรับชั้นเรียน functor (ซึ่งก่อน C ++ 11 อย่างมีนัยสำคัญ) และมีหน่วยความจำที่ไม่ปลอดภัยเท่ากัน: ถ้าคุณจับอะไรบางอย่างโดยการอ้างอิงและเรียกปิดหลังจากนั้นสิ่งที่จะตาย คุณจะได้รับ UB เหมือนกับการอ้างอิงนานกว่าที่ถูกต้อง การปรากฏตัวของพวกเขาล่าช้ากว่า 40 ปีนั้นเกิดจากการรับรู้ถึงความล่าช้าของ FP ไม่ได้เกิดจากการหาวิธีทำให้ปลอดภัย และในขณะที่การออกแบบพวกเขาเป็นงานที่ใหญ่โตอย่างแน่นอนฉันสงสัยว่าความพยายามส่วนใหญ่เข้าสู่การพิจารณาตลอดชีวิต

ฉันเห็นด้วยกับ delnan: C ++ ไม่ได้รับการปิดอย่างถูกต้อง: คุณต้องตั้งโปรแกรมอย่างระมัดระวังหากคุณไม่ต้องการรับ core dump เมื่อคุณเรียกใช้มัน
Giorgio

2
@delnan: แลมบ์ดาโดยการจับภาพอ้างอิงมากมีเจตนาที่[&]ไวยากรณ์ โปรแกรมเมอร์ C ++ ทุกคนจะเชื่อมโยง&เครื่องหมายกับการอ้างอิงแล้วและรู้เกี่ยวกับการอ้างอิงค้าง
MSalters

2
@MSalters ประเด็นของคุณคืออะไร? ฉันวาดการเชื่อมต่ออ้างอิงด้วยตัวเอง ฉันไม่ได้บอกว่า C ++ lambdas นั้นไม่ปลอดภัยเป็นพิเศษฉันบอกว่าพวกมันไม่ปลอดภัยเท่าการอ้างอิง ฉันไม่ได้แย้งว่าลูกแกะ C ++ ไม่ดีฉันโต้เถียงกับคำตอบของคำตอบนี้ (C ++ นั้นปิดตัวลงช้ามากเพราะพวกเขาต้องคิดหาวิธีที่ถูกต้อง)

5
  1. SBMM ทำให้โปรแกรมกำหนดค่าได้มากขึ้น (คุณสามารถบอกได้อย่างแม่นยำเมื่อวัตถุถูกทำลาย)

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

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

ดูusingใน C # และuseF #

  1. หน่วยความจำฮีปยังสามารถ (ขอบเขตอย่างสวยงามมาก imo) เป็นขอบเขต (ดู std :: shared_ptr ใน C ++)

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

ทำไม SBMM ถึงไม่ใช้กันอย่างแพร่หลาย? ข้อเสียของมันคืออะไร?

SBMM จำกัด สิ่งที่คุณสามารถทำได้:

  1. SBMM สร้างปัญหา funarg ที่สูงขึ้นด้วยการปิดคำศัพท์ชั้นหนึ่งซึ่งเป็นสาเหตุที่การปิดเป็นที่นิยมและใช้งานง่ายในภาษาเช่น C # แต่หายากและยุ่งยากใน C ++ โปรดทราบว่ามีแนวโน้มทั่วไปต่อการใช้โครงสร้างการทำงานในการเขียนโปรแกรม

  2. SBMM ต้องการ destructors และขัดขวางการโทรหางโดยเพิ่มงานที่ต้องทำเพิ่มเติมก่อนที่ฟังก์ชันจะส่งคืน การโทรหางมีประโยชน์สำหรับเครื่องที่ขยายได้และให้บริการโดยสิ่งต่างๆเช่น. NET

  3. โครงสร้างข้อมูลและอัลกอริธึมบางอย่างนั้นใช้งานยากโดยใช้ SBMM โดยทั่วไปทุกที่ที่รอบเกิดขึ้นตามธรรมชาติ ขั้นตอนวิธีกราฟที่สะดุดตาที่สุด คุณสามารถเขียน GC ของคุณเองได้อย่างมีประสิทธิภาพ

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

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

SBMM นั้น จำกัด ว่าโปรแกรมเมอร์ต้องการเส้นทางหลบหนีสำหรับสถานการณ์ที่อายุการใช้งานไม่สามารถทำได้ ใน C ++, shared_ptrข้อเสนอเส้นทางหลบหนี แต่ก็สามารถเป็น ~ 10 เท่าช้ากว่าการติดตามการเก็บขยะ ดังนั้นการใช้ SBMM แทน GC จะทำให้คนส่วนใหญ่ใช้เท้าผิดเวลาส่วนใหญ่ ที่ไม่ได้บอกว่ามันไร้ประโยชน์ SBMM ยังคงมีคุณค่าในบริบทของระบบและการเขียนโปรแกรมแบบฝังที่มีทรัพยากร จำกัด

FWIW คุณอาจต้องการดู Forth และ Ada และอ่านงานของ Nicolas Wirth


1
หากคุณพูดว่าส่วนใดที่ฉันอาจจะสามารถอธิบายรายละเอียดหรืออ้างอิงบทความ
Jon Harrop

2
ความเกี่ยวข้องของการใช้งานช้าลง 10 เท่าในกรณีการใช้งานที่หายากบางกรณีเมื่อเทียบกับการอยู่ทั่วไปทุกหนทุกแห่งในการใช้งานทุกกรณี? C ++ มี unique_ptr และสำหรับวัตถุประสงค์ส่วนใหญ่ก็เพียงพอแล้ว ถัดจากนั้นแทนที่จะโจมตีรางน้ำ RAII C ++ (ภาษาที่หลายคนชอบที่จะเกลียดเพราะเป็นภาษาโบราณ) หากคุณกำลังจะโจมตีรางน้ำ RAII โจมตีภาษาให้ลองน้องชายของตระกูล RAII เป็นสนิมตัวอย่างเช่น โดยทั่วไปแล้วสนิมทำให้ทุกอย่างถูกต้องที่ C ++ ผิดขณะที่ได้รับสิ่งที่ถูกต้องที่สุดที่ C ++ ทำถูกด้วย การใช้ 'เพิ่มเติม' จะทำให้คุณมีกรณีการใช้งานที่ จำกัด มาก
user1703394

2
"ความเกี่ยวข้องนั้นลดลง 10 เท่าในกรณีการใช้งานที่หายากเพียงไม่กี่กรณีเมื่อเทียบกับการอยู่ทั่วไปในทุกกรณีการใช้งาน" ประการแรกนั่นคืออาร์กิวเมนต์เวียน: shared_ptrหายากใน C ++ เท่านั้นเพราะมันช้ามาก ประการที่สองนั่นคือการเปรียบเทียบแอปเปิ้ลและส้ม (ตามบทความที่ฉันอ้างถึงแสดงให้เห็นแล้ว) เพราะshared_ptrช้ากว่าการผลิต GC หลายเท่า ประการที่สาม GCs ไม่ได้อยู่ทั่วไปทุกหนทุกแห่งและหลีกเลี่ยงในซอฟต์แวร์เช่น LMax และเอ็นจิ้น FIX ของ Rapid Addition
Jon Harrop

1
@ จอน Harrop ถ้าคุณไม่โปรดตรัสฉัน คุณใช้สูตรเวทอะไรในช่วงเวลา 30 ปีที่ผ่านมาเพื่อลดผลกระทบจากการใช้ทรัพยากรที่ลึก หากปราศจากสูตรเวทย์มนตร์เช่นนี้หลังจาก 30 ปีที่ผ่านมาฉันสามารถสรุปได้ว่าคุณต้องถูก misattributed ถูกกัดโดยสาเหตุอื่น ๆ
user1703394

1
@Jon Harrop, shared_ptr ไม่ได้หายากเพราะมันช้ามันหายากเพราะในระบบที่ออกแบบมาอย่างเหมาะสมความต้องการ 'การเป็นเจ้าของร่วม' นั้นหายาก
user1703394

4

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


1
"ความง่ายในการใช้งาน" แตกต่างจากระดับที่กำหนดอย่างไร ภาษาที่กำหนดขึ้นไม่ควรถูกนำมาใช้ให้ง่ายกว่าภาษาที่ไม่ได้กำหนดไว้หรือ
Philipp

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

อีกอย่างหนึ่ง "ภาษาสคริปต์" ที่หลากหลายเช่น Perl และ Python ยังใช้การนับการอ้างอิงเป็นวิธีการหลักในการจัดการหน่วยความจำ
Philipp

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

3

ข้อดีอย่างหนึ่งที่สำคัญของระบบ GC ซึ่งไม่มีใครได้กล่าวถึงเลยก็คือว่าการอ้างอิงในระบบ GC มีการรับประกันว่าจะรักษาเอกลักษณ์ของมันตราบเท่าที่มันมีอยู่ หากมีหนึ่งการเรียกIDisposable.Dispose(. NET) หรือAutoCloseable.Close(Java) บนวัตถุในขณะที่มีสำเนาของการอ้างอิงอยู่สำเนาเหล่านั้นจะยังคงอ้างอิงไปยังวัตถุเดียวกัน วัตถุจะไม่เป็นประโยชน์สำหรับสิ่งใดอีกต่อไป แต่ความพยายามที่จะใช้มันจะมีพฤติกรรมที่สามารถคาดเดาได้ซึ่งควบคุมโดยวัตถุเอง ในทางกลับกันใน C ++ หากโค้ดเรียกใช้deleteบนวัตถุและพยายามใช้งานในภายหลังสถานะทั้งหมดของระบบจะไม่ได้ถูกกำหนดอย่างสมบูรณ์

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


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

@delnan: เมื่อฉันดู rust-lang.org เบราว์เซอร์ของฉันไม่สามารถนำทางได้อย่างเป็นประโยชน์ ฉันควรหาข้อมูลเพิ่มเติมที่ไหน ความประทับใจของฉันคือความปลอดภัยของหน่วยความจำที่ไม่มี GC กำหนดข้อ จำกัด บางประการเกี่ยวกับโครงสร้างข้อมูลซึ่งอาจไม่เหมาะกับทุกสิ่งที่แอปพลิเคชันอาจต้องทำ แต่ฉันยินดีที่จะพิสูจน์ว่าผิด
supercat

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

@delnan: Rust ต้องการการปรับปรุงที่เชื่อมต่อกันเพื่อเคาน์เตอร์อ้างอิงหรือมีวิธีอื่นในการจัดการกับวัตถุที่ไม่เปลี่ยนรูปแบบ (หรือวัตถุที่เปลี่ยนแปลงไม่ได้ห่อหุ้มไม่ได้) ซึ่งไม่มีความเป็นเจ้าของที่แน่นอน? Rust มีแนวคิดเกี่ยวกับพอยน์เตอร์ "object-owning" และ "non-owning" หรือไม่? ฉันจำกระดาษหนึ่งฉบับเกี่ยวกับพอยน์เตอร์ "Xonor" ซึ่งกล่าวถึงแนวคิดของวัตถุที่มีการอ้างอิงเดียวที่ "เป็นเจ้าของ" พวกเขาและการอ้างอิงอื่น ๆ ที่ไม่มี เมื่อการอ้างอิง "เป็นเจ้าของ" ออกไปนอกขอบเขตการอ้างอิงที่ไม่ใช่เจ้าของทั้งหมดจะกลายเป็นการอ้างอิงไปยังวัตถุที่ตายแล้วและจะสามารถระบุได้เช่น ...
supercat

1
ฉันไม่คิดว่าความคิดเห็นกองซ้อนแลกเปลี่ยนเป็นสื่อที่เหมาะสมในการให้ทัวร์ผ่านภาษา หากคุณยังสนใจคุณสามารถตรงไปที่แหล่งที่มา (#rust IRC รายชื่อผู้รับจดหมายสนิมและอื่น ๆ ) และ / หรือตีฉันด้วยห้องสนทนา (คุณควรสร้างได้)

-2

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

โพสต์บล็อกต่อไปนี้กล่าวถึงประเด็นสำคัญของ RAII และเปรียบเทียบกับการแก้ไขทรัพยากรในภาษา GCed ที่ใช้ GC ที่ไม่ได้กำหนดไว้ล่วงหน้า

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

เป็นเรื่องสำคัญที่จะต้องทราบว่าในขณะที่ RAII ส่วนใหญ่ใช้ใน C ++, Python (ในที่สุดไม่ใช่รุ่นที่ใช้ VM) มี destructors และกำหนดค่า GC ที่อนุญาตให้ RAII ใช้ร่วมกับ GC ดีที่สุดของโลกทั้งสองถ้าเป็น


1
-1 นั่นเป็นหนึ่งในบทความที่เลวร้ายที่สุดที่ฉันเคยอ่าน
Jon Harrop

1
ปัญหาในภาษาไม่ใช่ว่าพวกเขาสนับสนุน GC แต่พวกเขาละทิ้ง RAII ไม่มีเหตุผลที่ภาษา / กรอบงานไม่ควรสนับสนุนทั้งคู่
supercat

1
@ จอน Harrop คุณช่วยอธิบายได้ไหม จากการอ้างสิทธิ์ในบทความมีการอ้างสิทธิ์ 3 ข้อแรกที่ไม่ถือ ฉันคิดว่าคุณอาจไม่เห็นด้วยกับการอ้างสิทธิ์ในการเพิ่มประสิทธิภาพ แต่อีก 3 ข้อเรียกร้องนั้นใช้ได้จริง ๆ สิ่งสำคัญที่สุดคือสิ่งแรกที่เกี่ยวกับความแปรปรวนของการเป็นทรัพยากร
user1703394

2
@ user1703394: ประการแรกบทความทั้งหมดมีพื้นฐานมาจากฟาง "ภาษา GCed" เมื่ออันที่จริงไม่มีอะไรเกี่ยวข้องกับการเก็บขยะ ประการที่สองเขาโทษการเก็บขยะเมื่อในความเป็นจริงข้อผิดพลาดอยู่กับการเขียนโปรแกรมเชิงวัตถุ ในที่สุดข้อโต้แย้งของเขาคือ 10 ปีสายเกินไป โปรแกรมเมอร์ส่วนใหญ่มีความทันสมัยกับภาษาที่รวบรวมขยะอย่างแม่นยำเพราะให้ประสิทธิภาพที่สูงกว่ามาก
Jon Harrop

1
ตัวอย่างที่เป็นรูปธรรมของเขา (RAM, การจัดการไฟล์ที่เปิด, ล็อค, เธรด) ค่อนข้างบอกได้ ฉันกดยากที่จะจำครั้งสุดท้ายที่ฉันต้องเขียนโค้ดที่เกี่ยวข้องโดยตรงกับสิ่งเหล่านั้น เมื่อใช้ RAM, GC จะทำทุกอย่างโดยอัตโนมัติ ด้วยการจัดการไฟล์ฉันเขียนโค้ดเช่นเดียวกับFile.ReadLines file |> Seq.lengthที่ abstractions จัดการปิดสำหรับฉัน ล็อคและหัวข้อที่ฉันได้ถูกแทนที่ด้วย .NET Taskและ F MailboxProcessor# "เราขยายปริมาณการจัดการทรัพยากรด้วยตนเองทั้งหมดนี้" เป็นเรื่องไร้สาระที่สมบูรณ์
Jon Harrop
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.