เพื่อเพิ่มการอภิปรายที่นี่
มีปัญหาที่ทราบเกี่ยวกับการรวบรวมขยะและการทำความเข้าใจช่วยให้เข้าใจว่าทำไมไม่มีใน C ++
1. ประสิทธิภาพ?
การร้องเรียนครั้งแรกมักจะเกี่ยวกับประสิทธิภาพ แต่คนส่วนใหญ่ไม่ได้ตระหนักถึงสิ่งที่พวกเขากำลังพูดถึง ตามที่แสดงโดยMartin Beckett
ปัญหาอาจไม่ได้ประสิทธิภาพต่อ se แต่เป็นการคาดการณ์ของประสิทธิภาพ
ปัจจุบันมีตระกูล GC 2 ตระกูลที่ปรับใช้อย่างกว้างขวาง:
- ประเภท Mark-And-Sweep
- ชนิดการอ้างอิงการอ้างอิง
Mark And Sweep
เร็ว (ผลกระทบน้อยลงในการปฏิบัติงานโดยรวม) แต่มันทนทุกข์ทรมานจาก "หยุดโลก" ซินโดรม: คือเมื่อได้ลูกเตะ GC ในทุกอย่างอื่นจะหยุดการทำงานจนกว่า GC ได้ทำให้การทำความสะอาดของ หากคุณต้องการสร้างเซิร์ฟเวอร์ที่ตอบสนองในไม่กี่มิลลิวินาที ... ธุรกรรมบางอย่างจะไม่เป็นไปตามที่คุณคาดหวัง :)
ปัญหาที่Reference Counting
แตกต่าง: การนับการอ้างอิงเพิ่มค่าใช้จ่ายโดยเฉพาะอย่างยิ่งในสภาพแวดล้อม Multi-Threading เพราะคุณต้องมีการนับอะตอม นอกจากนี้ยังมีปัญหาของรอบการอ้างอิงดังนั้นคุณต้องใช้อัลกอริทึมที่ฉลาดในการตรวจสอบรอบเหล่านั้นและกำจัดพวกมัน (โดยทั่วไปจะใช้โดย "แช่แข็งโลก" ด้วยเช่นกัน แต่บ่อยครั้งน้อยกว่า) โดยทั่วไปแล้ว ณ วันนี้ประเภทนี้ (แม้ว่าตามปกติมากขึ้นการตอบสนองหรือค่อนข้างน้อยมักจะแช่แข็ง) Mark And Sweep
จะช้ากว่า
ฉันเคยเห็นกระดาษจากผู้ดำเนินการของไอเฟลที่พยายามใช้งานReference Counting
Garbage Collector ซึ่งจะมีประสิทธิภาพระดับโลกที่คล้ายคลึงกันMark And Sweep
โดยไม่ต้องมี "แช่แข็งโลก" มันต้องการเธรดแยกต่างหากสำหรับ GC (ทั่วไป) อัลกอริทึมนั้นค่อนข้างน่ากลัว (ในตอนท้าย) แต่กระดาษก็ทำหน้าที่ได้ดีในการนำแนวคิดหนึ่งครั้งและแสดงวิวัฒนาการของอัลกอริทึมจากเวอร์ชั่น "ง่าย" ไปจนถึงเวอร์ชันเต็ม แนะนำให้อ่านถ้าฉันสามารถวางมือบนไฟล์ PDF ...
2. การจัดหาทรัพยากรเป็นการเริ่มต้น (RAII)
เป็นสำนวนทั่วไปC++
ที่คุณจะห่อกรรมสิทธิ์ในทรัพยากรภายในวัตถุเพื่อให้แน่ใจว่าพวกมันถูกปล่อยออกมาอย่างถูกต้อง ส่วนใหญ่จะใช้สำหรับหน่วยความจำเนื่องจากเราไม่มีการรวบรวมขยะ แต่ก็มีประโยชน์อย่างไรก็ตามสำหรับสถานการณ์อื่น ๆ :
- ล็อค (หลายเธรดจัดการไฟล์ ... )
- การเชื่อมต่อ (ไปยังฐานข้อมูลเซิร์ฟเวอร์อื่น ... )
แนวคิดคือการควบคุมอายุการใช้งานของวัตถุอย่างถูกต้อง:
- มันควรจะมีชีวิตอยู่ตราบเท่าที่คุณต้องการ
- มันควรจะถูกฆ่าตายเมื่อคุณทำเสร็จแล้ว
ปัญหาของ GC คือถ้ามันช่วยได้กับอดีตและในที่สุดก็รับประกันได้ว่าในภายหลัง ... "สุดยอด" นี้อาจไม่เพียงพอ หากคุณปล่อยล็อคคุณต้องการให้มันปลดล็อคในขณะนี้เพื่อที่จะไม่ได้บล็อคการโทรเพิ่มเติม!
ภาษาที่มี GC มีสองวิธีแก้ไข:
- อย่าใช้ GC เมื่อการจัดสรรสแต็กเพียงพอ: โดยปกติจะเป็นปัญหาด้านประสิทธิภาพ แต่ในกรณีของเรามันช่วยได้จริงเนื่องจากขอบเขตจะกำหนดอายุการใช้งาน
using
สร้าง ... แต่ชัดเจน (อ่อนแอ) RAII ขณะอยู่ใน C ++ RAII โดยปริยายเพื่อให้ผู้ใช้ไม่สามารถสร้างข้อผิดพลาดได้โดยไม่เจตนา (โดยไม่ต้องใส่using
คำหลัก)
3. พอยน์เตอร์อัจฉริยะ
ชี้สมาร์ทมักจะปรากฏเป็น bullet C++
เงินกับหน่วยความจำในการจัดการ บ่อยครั้งที่ฉันได้ยิน: เราไม่ต้องการ GC เพราะเรามีพอยน์เตอร์อัจฉริยะ
หนึ่งอาจไม่ผิดมากขึ้น
ตัวชี้สมาร์ทช่วยauto_ptr
และunique_ptr
ใช้แนวคิด RAII ซึ่งมีประโยชน์อย่างยิ่ง มันง่ายมากที่คุณสามารถเขียนมันเองได้ง่ายๆ
เมื่อต้องการแชร์ความเป็นเจ้าของ แต่มันก็ยากขึ้น: คุณอาจแชร์หลายเธรดและมีปัญหาเล็กน้อยบางประการเกี่ยวกับการจัดการการนับ shared_ptr
ดังนั้นหนึ่งตามธรรมชาติไปสู่การ
มันยอดเยี่ยมนั่นคือสิ่งที่ Boost for after แต่ไม่ใช่ bullet เงิน ในความเป็นจริงปัญหาหลักของshared_ptr
มันคือมันเลียนแบบ GC ที่นำมาใช้Reference Counting
แต่คุณต้องใช้การตรวจจับรอบทั้งหมดด้วยตัวเอง ... Urg
แน่นอนว่ามีสิ่งนี้weak_ptr
แต่ฉันโชคไม่ดีที่ได้เห็นการรั่วไหลของหน่วยความจำแม้จะมีการใช้shared_ptr
เพราะรอบเหล่านั้น ... และเมื่อคุณอยู่ในสภาพแวดล้อมแบบมัลติเธรดมันยากที่จะตรวจจับ!
4. อะไรคือทางออก?
ไม่มีกระสุนสีเงิน แต่ก็เป็นไปได้อย่างแน่นอน ในกรณีที่ไม่มี GC หนึ่งจะต้องชัดเจนในการเป็นเจ้าของ:
- ต้องการมีเจ้าของคนเดียวในเวลาที่กำหนดถ้าเป็นไปได้
- ถ้าไม่มีให้แน่ใจว่าแผนภาพคลาสของคุณไม่มีวงจรใด ๆ ที่เกี่ยวข้องกับการเป็นเจ้าของและทำลายพวกเขาด้วยแอปพลิเคชันที่ละเอียดอ่อนของ
weak_ptr
ดังนั้นจะเป็นการดีถ้ามี GC ... แต่มันไม่มีปัญหาเล็กน้อย และในเวลานั้นเราแค่ต้องพับแขนเสื้อของเรา