อ่าน 10 นาที
การจัดการขยะ (วิทยาการคอมพิวเตอร์)
ใน วิทยาการคอมพิวเตอร์ การ เก็บขยะ ( GC ) เป็นรูปแบบหนึ่งของการจัดการหน่วยความจำอัตโนมัติ[ 2 ] ตัว เก็บ ขยะ พยายาม เรียกคืนหน่วยความจำที่โปรแกรมจัดสรรไว้...
การจัดการขยะ (วิทยาการคอมพิวเตอร์)


ในวิทยาการคอมพิวเตอร์การเก็บขยะ ( GC ) เป็นรูปแบบหนึ่งของการจัดการหน่วยความจำอัตโนมัติ[ 2 ]ตัวเก็บขยะพยายามเรียกคืนหน่วยความจำที่โปรแกรมจัดสรรไว้ แต่ไม่ได้ถูกอ้างอิงอีกต่อไป หน่วยความจำดังกล่าวเรียกว่าขยะการเก็บขยะถูกคิดค้นโดยนักวิทยาศาสตร์คอมพิวเตอร์ชาวอเมริกันJohn McCarthyประมาณปี 1959 เพื่อลดความซับซ้อนของการจัดการหน่วยความจำด้วยตนเองในภาษาLisp [ 3 ]
การเก็บขยะช่วยลดภาระของโปรแกรมเมอร์ในการจัดการหน่วยความจำด้วยตนเองซึ่งโปรแกรมเมอร์ต้องระบุว่าวัตถุใดควรยกเลิกการจัดสรรและส่งคืนให้กับระบบหน่วยความจำ และเมื่อใดจึงจะทำเช่นนั้น[ 2 ]เทคนิคอื่นๆ ที่คล้ายกัน ได้แก่การจัดสรรสแต็กการอนุมานภูมิภาคและการเป็นเจ้าของหน่วยความจำ และการผสมผสานของเทคนิคเหล่านี้ การเก็บขยะอาจใช้เวลาส่วนใหญ่ของเวลาประมวลผลทั้งหมดของโปรแกรม และส่งผลต่อประสิทธิภาพ การทำงาน
ทรัพยากรอื่นๆ นอกเหนือจากหน่วยความจำ เช่นซ็อกเก็ตเครือข่ายตัวจัดการฐานข้อมูลหน้าต่าง ตัวอธิบายไฟล์และตัวอธิบายอุปกรณ์ โดยทั่วไปแล้วจะไม่ถูกจัดการโดยระบบเก็บขยะ แต่จะถูกจัดการโดยวิธีการอื่นๆ( เช่นตัวทำลาย ) ซึ่งบางวิธีการเหล่านี้ก็มีการคืนหน่วยความจำด้วยเช่นกัน
ภาพรวม
ภาษาโปรแกรมหลาย ภาษา จำเป็นต้องมีการเก็บขยะ ไม่ว่าจะเป็นส่วนหนึ่งของข้อกำหนดของภาษา (เช่นRPL , Java , C# , D , [ 4 ] Goและภาษาสคริปต์ ส่วนใหญ่ ) หรือเพื่อการใช้งานจริง (เช่น ภาษาทางการอย่างแคลคูลัสแลมบ์ดา ) [ 5 ] ภาษา เหล่านี้เรียกว่าภาษาที่มีการเก็บขยะภาษาอื่นๆ เช่นCและC++ถูกออกแบบมาเพื่อใช้กับการจัดการหน่วยความจำด้วยตนเอง แต่ก็มีการใช้งานที่มีการเก็บขยะให้เลือกใช้ ภาษาบางภาษา เช่นAda , Modula-3และC++/CLIอนุญาตให้ทั้งการเก็บขยะและการจัดการหน่วยความจำด้วยตนเองทำงานร่วมกันได้ในแอปพลิเคชันเดียวกัน โดยใช้ฮีป แยกต่างหาก สำหรับวัตถุที่ถูกเก็บขยะและวัตถุที่จัดการด้วยตนเอง ภาษาอื่นๆ เช่นDมีการเก็บขยะ แต่ผู้ใช้สามารถลบวัตถุด้วยตนเองหรือปิดใช้งานการเก็บขยะได้ทั้งหมดเมื่อต้องการความเร็ว[ 6 ]
แม้ว่าหลายภาษาจะรวม GC เข้ากับคอมไพเลอร์และระบบรันไทม์แต่ ก็ยังมีระบบ GC แบบ post-hocอยู่ เช่นAutomatic Reference Counting (ARC) ระบบ GC แบบ post-hocบางระบบไม่จำเป็นต้องคอมไพล์ใหม่[ 7 ]
ข้อดี
GC ช่วยให้โปรแกรมเมอร์ไม่ต้องยกเลิกการจัดสรรหน่วยความจำด้วยตนเอง ซึ่งจะช่วยหลีกเลี่ยงข้อผิดพลาด บางประเภทได้ : [ 8 ]
- พอยเตอร์ค้างซึ่งเกิดขึ้นเมื่อหน่วยความจำส่วนหนึ่งถูกปล่อยในขณะที่ยังมีพอยเตอร์ชี้ไปยังหน่วยความจำนั้นอยู่ และพอยเตอร์ตัวใดตัวหนึ่งถูกอ้างอิงถึง ในขณะนั้นหน่วยความจำอาจถูกจัดสรรให้กับการใช้งานอื่นแล้ว ซึ่งอาจส่งผลที่คาดเดาไม่ได้ [ 9 ]
- ข้อผิดพลาดการปลดปล่อยซ้ำซ้อน (Double free bugs ) เกิดขึ้นเมื่อโปรแกรมพยายามปลดปล่อยพื้นที่หน่วยความจำที่ถูกปลดปล่อยไปแล้ว และอาจถูกจัดสรรใหม่ไปแล้วด้วย
- หน่วยความจำรั่วไหลบางประเภทซึ่งโปรแกรมไม่สามารถปลดปล่อยหน่วยความจำที่ถูกครอบครองโดยวัตถุที่ไม่สามารถเข้าถึงได้ซึ่งอาจนำไปสู่การหมดหน่วยความจำ[ 10 ]
ข้อเสีย
GC ใช้ทรัพยากรการคำนวณเพื่อตัดสินใจว่าจะปล่อยหน่วยความจำใด ดังนั้น ค่าใช้จ่ายสำหรับความสะดวกสบายในการไม่ระบุอายุการใช้งานของวัตถุด้วยตนเองในซอร์สโค้ดคือโอเวอร์เฮดซึ่งอาจส่งผลเสียต่อประสิทธิภาพของโปรแกรม[ 11 ]บทความวิจัยที่ได้รับการตรวจสอบโดยผู้ทรงคุณวุฒิในปี 2548 สรุปว่า GC ต้องการหน่วยความจำมากกว่าถึงห้าเท่าเพื่อชดเชยโอเวอร์เฮดนี้และเพื่อให้ทำงานได้เร็วเท่ากับโปรแกรมเดียวกันที่ใช้การจัดการหน่วยความจำแบบชัดเจนในอุดมคติ อย่างไรก็ตาม การเปรียบเทียบนี้ทำกับโปรแกรมที่สร้างขึ้นโดยการแทรกการเรียกยกเลิกการจัดสรรโดยใช้ออราเคิลซึ่งดำเนินการโดยการรวบรวมร่องรอยจากโปรแกรมที่ทำงานภายใต้โปรไฟล์เลอร์และโปรแกรมนั้นถูกต้องเฉพาะสำหรับการดำเนินการโปรแกรมครั้งใดครั้งหนึ่งเท่านั้น[ 12 ]ปฏิสัมพันธ์กับ เอฟเฟกต์ ลำดับชั้นของหน่วยความจำอาจทำให้โอเวอร์เฮดนี้ทนไม่ได้ในสถานการณ์ที่ยากต่อการคาดการณ์หรือตรวจจับในการทดสอบตามปกติ ผลกระทบต่อประสิทธิภาพนี้ถูกอ้างโดย Apple ว่าเป็นเหตุผลที่ไม่นำการเก็บขยะมาใช้ในiOSแม้ว่าจะเป็นคุณสมบัติที่ต้องการมากที่สุดก็ตาม[ 13 ]
ช่วงเวลาที่ระบบจะทำการเก็บกวาดขยะนั้นอาจคาดเดาไม่ได้ ส่งผลให้เกิดการหยุดชะงัก (การหยุดชั่วคราวเพื่อย้าย/ปล่อยหน่วยความจำ) กระจายอยู่ตลอดเซสชันการหยุดชะงักที่คาดเดาไม่ได้นั้นเป็นสิ่งที่ยอมรับไม่ได้ในสภาพแวดล้อมแบบเรียลไทม์ในการประมวลผลธุรกรรมหรือในโปรแกรมแบบโต้ตอบ ตัวเก็บกวาดขยะแบบเพิ่มทีละน้อย แบบพร้อมกัน และแบบเรียลไทม์ สามารถแก้ไขปัญหาเหล่านี้ได้ โดยมีข้อดีข้อเสียที่แตกต่างกันไป
กลยุทธ์
การติดตาม
การติดตามการเก็บขยะเป็นวิธีการเก็บขยะที่พบได้บ่อยที่สุด จนกระทั่งคำว่า "การเก็บขยะ" มักหมายถึงการติดตามการเก็บขยะมากกว่าวิธีการอื่นๆ เช่นการนับการอ้างอิงกลยุทธ์โดยรวมประกอบด้วยการพิจารณาว่าวัตถุใดควรถูกเก็บขยะโดยการติดตามว่าวัตถุใดสามารถเข้าถึงได้โดยห่วงโซ่การอ้างอิงจากวัตถุรากบางตัว และพิจารณาส่วนที่เหลือเป็นขยะและเก็บขยะเหล่านั้น[ 14 ]อย่างไรก็ตาม มีอัลกอริธึมจำนวนมากที่ใช้ในการใช้งาน โดยมีความซับซ้อนและลักษณะการทำงานที่แตกต่างกันอย่างมาก
การนับอ้างอิง
การเก็บขยะแบบนับการอ้างอิงคือการที่แต่ละวัตถุมีการนับจำนวนการอ้างอิงถึงวัตถุนั้น ขยะจะถูกระบุโดยการมีจำนวนการอ้างอิงเป็นศูนย์ จำนวนการอ้างอิงของวัตถุจะเพิ่มขึ้นเมื่อมีการสร้างการอ้างอิงถึงวัตถุนั้น และจะลดลงเมื่อมีการทำลายการอ้างอิง เมื่อจำนวนการอ้างอิงถึงศูนย์ หน่วยความจำของวัตถุนั้นจะถูกเรียกคืน[ 15 ]
เช่นเดียวกับการจัดการหน่วยความจำด้วยตนเอง และแตกต่างจากการติดตามการเก็บขยะ การนับการอ้างอิงรับประกันว่าวัตถุจะถูกทำลายทันทีที่การอ้างอิงครั้งสุดท้ายถูกทำลาย และโดยปกติจะเข้าถึงหน่วยความจำเฉพาะที่อยู่ในแคชของ CPUในวัตถุที่จะถูกปล่อย หรือถูกชี้โดยตรงโดยวัตถุเหล่านั้นเท่านั้น ดังนั้นจึงมักไม่มีผลข้างเคียงเชิงลบที่สำคัญต่อการทำงาน ของแคช CPU และ หน่วยความจำเสมือน
การนับจำนวนการอ้างอิงมีข้อเสียอยู่หลายประการ ซึ่งโดยทั่วไปแล้วสามารถแก้ไขหรือลดผลกระทบได้ด้วยอัลกอริธึมที่ซับซ้อนกว่า:
- วงจร
- หากวัตถุสองชิ้นขึ้นไปอ้างอิงถึงกันและกัน วัตถุเหล่านั้นอาจสร้างวงจรขึ้นมา ซึ่งจะทำให้ไม่มีวัตถุใดถูกเก็บรวบรวม เนื่องจากการอ้างอิงซึ่งกันและกันจะไม่ทำให้จำนวนการอ้างอิงกลายเป็นศูนย์ ระบบการเก็บขยะบางระบบที่ใช้การนับการอ้างอิง (เช่น ระบบในCPython ) ใช้ขั้นตอนวิธีตรวจจับวงจรเฉพาะเพื่อจัดการกับปัญหานี้[ 16 ]กลยุทธ์อีกอย่างหนึ่งคือการใช้การอ้างอิงแบบอ่อนสำหรับ "backpointer" ซึ่งสร้างวงจร ภายใต้การนับการอ้างอิง การอ้างอิงแบบอ่อนจะคล้ายกับการอ้างอิงแบบอ่อนภายใต้ตัวเก็บขยะแบบติดตาม มันเป็นวัตถุอ้างอิงพิเศษที่การมีอยู่ของมันไม่ได้เพิ่มจำนวนการอ้างอิงของวัตถุที่ถูกอ้างอิง ยิ่งไปกว่านั้น การอ้างอิงแบบอ่อนมีความปลอดภัยตรงที่เมื่อวัตถุที่ถูกอ้างอิงกลายเป็นขยะ การอ้างอิงแบบอ่อนใดๆ ก็ตามจะหมดอายุลงแทนที่จะได้รับอนุญาตให้คงอยู่แบบค้างอยู่ ซึ่งหมายความว่ามันจะกลายเป็นค่าที่คาดเดาได้ เช่น การอ้างอิงที่เป็นค่าว่าง
- ค่าใช้จ่ายด้านพื้นที่ (จำนวนการอ้างอิง)
- การนับการอ้างอิง (Reference counting) จำเป็นต้องจัดสรรพื้นที่สำหรับแต่ละอ็อบเจ็กต์เพื่อเก็บจำนวนการอ้างอิง จำนวนการอ้างอิงอาจถูกเก็บไว้ใกล้กับหน่วยความจำของอ็อบเจ็กต์หรือในตารางด้านข้างที่อื่น แต่ไม่ว่าในกรณีใด ทุกอ็อบเจ็กต์ที่มีการนับการอ้างอิงจำเป็นต้องมีพื้นที่จัดเก็บเพิ่มเติมสำหรับจำนวนการอ้างอิง โดยทั่วไปจะใช้พื้นที่หน่วยความจำขนาดเท่ากับพอยเตอร์แบบไม่ระบุเครื่องหมาย (unsigned pointer) สำหรับงานนี้ ซึ่งหมายความว่าต้องจัดสรรพื้นที่จัดเก็บจำนวนการอ้างอิง 32 หรือ 64 บิตสำหรับแต่ละอ็อบเจ็กต์ ในบางระบบ อาจสามารถลดภาระนี้ได้โดยใช้พอยเตอร์แบบแท็ก (tagged pointer)เพื่อเก็บจำนวนการอ้างอิงในพื้นที่ที่ไม่ได้ใช้งานของหน่วยความจำของอ็อบเจ็กต์ บ่อยครั้งที่สถาปัตยกรรมไม่อนุญาตให้โปรแกรมเข้าถึงช่วงของที่อยู่หน่วยความจำทั้งหมดที่สามารถจัดเก็บได้ในขนาดพอยเตอร์ดั้งเดิม บิตสูงจำนวนหนึ่งในที่อยู่จะถูกละเลยหรือต้องเป็นศูนย์ หากอ็อบเจ็กต์มีพอยเตอร์อยู่ที่ตำแหน่งใดตำแหน่งหนึ่งอย่างน่าเชื่อถือ จำนวนการอ้างอิงสามารถเก็บไว้ในบิตที่ไม่ได้ใช้งานของพอยเตอร์ได้ ตัวอย่างเช่น แต่ละอ็อบเจ็กต์ในObjective-Cมีพอยเตอร์ไปยังคลาส ของมัน ที่จุดเริ่มต้นของหน่วยความจำ บนสถาปัตยกรรมARM64 ที่ใช้ iOS 7บิตที่ไม่ได้ใช้ 19 บิตของตัวชี้คลาสนี้ถูกใช้เพื่อเก็บจำนวนการอ้างอิงของวัตถุ[ 17 ] [ 18 ]
- ค่าใช้จ่ายเพิ่มเติมด้านความเร็ว (เพิ่ม/ลด)
- ในการใช้งานแบบง่ายๆ การกำหนดค่าอ้างอิงแต่ละครั้งและการอ้างอิงแต่ละครั้งที่อยู่นอกขอบเขตมักต้องมีการแก้ไขตัวนับการอ้างอิงอย่างน้อยหนึ่งตัว อย่างไรก็ตาม ในกรณีทั่วไป เมื่อมีการคัดลอกการอ้างอิงจากตัวแปรขอบเขตภายนอกไปยังตัวแปรขอบเขตภายใน โดยที่อายุการใช้งานของตัวแปรภายในถูกจำกัดด้วยอายุการใช้งานของตัวแปรภายนอก การเพิ่มการอ้างอิงสามารถถูกกำจัดได้ ตัวแปรภายนอก "เป็นเจ้าของ" การอ้างอิง ในภาษาโปรแกรม C++ เทคนิคนี้ถูกนำไปใช้และสาธิตได้อย่างง่ายดายโดยใช้
constการอ้างอิง การนับการอ้างอิงใน C++ มักจะถูกนำไปใช้โดยใช้ " ตัวชี้อัจฉริยะ " [ 19 ]ซึ่งตัวสร้าง ตัวทำลาย และตัวดำเนินการกำหนดค่าจะจัดการการอ้างอิง ตัวชี้อัจฉริยะสามารถส่งผ่านโดยการอ้างอิงไปยังฟังก์ชัน ซึ่งหลีกเลี่ยงความจำเป็นในการสร้างตัวชี้อัจฉริยะใหม่ (ซึ่งจะเพิ่มจำนวนการอ้างอิงเมื่อเข้าสู่ฟังก์ชันและลดลงเมื่อออก) แทนที่จะเป็นเช่นนั้น ฟังก์ชันจะได้รับการอ้างอิงไปยังตัวชี้อัจฉริยะซึ่งสร้างขึ้นอย่างประหยัด วิธีการนับการอ้างอิงแบบ Deutsch-Bobrow ใช้ประโยชน์จากข้อเท็จจริงที่ว่าการอัปเดตการนับการอ้างอิงส่วนใหญ่ถูกสร้างขึ้นโดยการอ้างอิงที่เก็บไว้ในตัวแปรโลคอล โดยจะละเว้นการอ้างอิงเหล่านี้ และนับเฉพาะการอ้างอิงในฮีปเท่านั้น แต่ก่อนที่จะลบวัตถุที่มีการนับการอ้างอิงเป็นศูนย์ ระบบจะต้องตรวจสอบด้วยการสแกนสแต็กและรีจิสเตอร์ว่าไม่มีการอ้างอิงอื่นใดที่ยังคงอยู่ การลดโอเวอร์เฮดในการอัปเดตตัวนับลงอย่างมากสามารถทำได้โดยการรวมการอัปเดตที่ Levanoni และPetrank นำ เสนอ[ 20 ] [ 21 ]พิจารณาพอยเตอร์ที่ได้รับการอัปเดตหลายครั้งในช่วงเวลาการทำงานที่กำหนด โดยจะชี้ไปยังวัตถุO1จากนั้นไปยังวัตถุO2และอื่นๆ จนกระทั่งเมื่อสิ้นสุดช่วงเวลาจะชี้ไปยังวัตถุบางอย่าง โดยOnทั่วไปแล้วอัลกอริทึมการนับการอ้างอิงจะดำเนินการrc(O1)--,rc(O2)++,rc(O2)--,rc(O3)++,rc(O3)--, ..., แต่การอัปเดตส่วนใหญ่เหล่านี้ซ้ำซ้อน เพื่อให้การนับการอ้างอิงได้รับการประเมินอย่างถูกต้องเมื่อสิ้นสุดช่วงเวลา การดำเนินการ และrc(On)++ก็เพียงพอแล้ว Levanoni และ Petrank วัดได้ว่าการอัปเดตตัวนับในเกณฑ์มาตรฐาน Java ทั่วไปลดลงมากกว่า 99%rc(O1)--rc(On)++
- ต้องใช้ความเป็นอะตอม
- เมื่อใช้งานใน สภาพแวดล้อม แบบมัลติเธรดการแก้ไขเหล่านี้ (การเพิ่มและการลดค่า) อาจจำเป็นต้องเป็นการดำเนินการอะตอมิกเช่นการเปรียบเทียบและการสลับอย่างน้อยสำหรับวัตถุใดๆ ที่ใช้ร่วมกัน หรืออาจใช้ร่วมกันระหว่างหลายเธรด การดำเนินการอะตอมิกมีค่าใช้จ่ายสูงในระบบมัลติโปรเซสเซอร์ และมีค่าใช้จ่ายสูงขึ้นไปอีกหากต้องจำลองด้วยอัลกอริธึมซอฟต์แวร์ เป็นไปได้ที่จะหลีกเลี่ยงปัญหานี้โดยการเพิ่มจำนวนการอ้างอิงต่อเธรดหรือต่อ CPU และเข้าถึงจำนวนการอ้างอิงทั่วโลกเฉพาะเมื่อจำนวนการอ้างอิงในพื้นที่กลายเป็นศูนย์หรือไม่เป็นศูนย์อีกต่อไป (หรืออีกทางเลือกหนึ่งคือการใช้ต้นไม้ไบนารีของจำนวนการอ้างอิง หรือแม้กระทั่งการละทิ้งการทำลายแบบกำหนดได้เพื่อแลกกับการไม่มีจำนวนการอ้างอิงทั่วโลกเลย) แต่สิ่งนี้จะเพิ่มภาระหน่วยความจำอย่างมากและมักจะมีประโยชน์เฉพาะในกรณีพิเศษเท่านั้น (ตัวอย่างเช่น ใช้ในการนับการอ้างอิงของโมดูลเคอร์เนล Linux) การรวมการอัปเดตโดย Levanoni และ Petrank [ 20 ] [ 21 ]สามารถใช้เพื่อกำจัดการดำเนินการอะตอมิกทั้งหมดออกจาก write-barrier ได้ ตัวนับจะไม่ได้รับการอัปเดตโดยเธรดของโปรแกรมในระหว่างการทำงานของโปรแกรม ตัวนับจะถูกแก้ไขโดยตัวเก็บรวบรวมเท่านั้น ซึ่งทำงานเสมือนเป็นเธรดเพิ่มเติมเพียงเธรดเดียวโดยไม่มีการซิงโครไนซ์ วิธีนี้สามารถใช้เป็นกลไกหยุดการทำงานทั้งหมดสำหรับโปรแกรมแบบขนาน และยังสามารถใช้กับตัวเก็บรวบรวมการนับอ้างอิงแบบพร้อมกันได้อีกด้วย
- ไม่ใช่แบบเรียลไทม์
- การใช้งานการนับการอ้างอิงแบบพื้นฐานมักไม่สามารถทำงานแบบเรียลไทม์ได้ เนื่องจาก1 การกำหนดค่าตัวชี้ใดๆ อาจทำให้วัตถุจำนวนหนึ่งซึ่งจำกัดด้วยขนาดหน่วยความจำที่จัดสรรไว้ทั้งหมดถูกปล่อยให้ว่างซ้ำๆ ในขณะที่เธรดไม่สามารถทำงานอื่นๆ ได้ สามารถหลีกเลี่ยงปัญหานี้ได้โดยการมอบหมายให้เธรดอื่นปล่อยวัตถุที่ไม่มีการอ้างอิง แต่ก็ต้องแลกมาด้วยค่าใช้จ่ายเพิ่มเติม
การวิเคราะห์การหลบหนี
การวิเคราะห์การหลุดรอด (Escape analysis)เป็นเทคนิคในระหว่างการคอมไพล์ที่สามารถแปลงการจัดสรรฮีปเป็นการจัดสรรสแต็กซึ่งจะช่วยลดปริมาณการเก็บขยะ (garbage collection) การวิเคราะห์นี้จะตรวจสอบว่าวัตถุที่จัดสรรภายในฟังก์ชันสามารถเข้าถึงได้จากภายนอกหรือไม่ หากพบว่าการจัดสรรภายในฟังก์ชันสามารถเข้าถึงได้จากฟังก์ชันหรือเธรดอื่น การจัดสรรนั้นจะเรียกว่า "หลุดรอด" (escape) และไม่สามารถทำบนสแต็กได้ มิฉะนั้น วัตถุอาจถูกจัดสรรโดยตรงบนสแต็กและปล่อยเมื่อฟังก์ชันส่งคืนค่า โดยข้ามฮีปและค่าใช้จ่ายในการจัดการหน่วยความจำที่เกี่ยวข้อง[ 22 ]
ความพร้อมใช้งาน
โดยทั่วไปแล้วภาษาโปรแกรมระดับสูงมักจะมีระบบจัดการขยะ (garbage collection) เป็นคุณสมบัติมาตรฐาน ในบางภาษาที่ไม่มีระบบจัดการขยะในตัว ก็สามารถเพิ่มเข้าไปได้ผ่านไลบรารี เช่นเดียวกับตัวจัดการขยะ Boehmสำหรับภาษา C และ C++
ภาษาการเขียนโปรแกรมเชิงฟังก์ชันส่วนใหญ่เช่นML , HaskellและAPLมีการเก็บขยะในตัวLispโดดเด่นเป็นพิเศษในฐานะที่เป็นภาษาการเขียนโปรแกรมเชิงฟังก์ชัน ภาษาแรก และเป็นภาษาแรกที่แนะนำการเก็บขยะ[ 23 ]
ภาษาแบบไดนามิกอื่นๆ เช่นRubyและJulia (แต่ไม่ใช่Perl 5 หรือPHPก่อนเวอร์ชัน 5.3 [ 24 ]ซึ่งทั้งสองใช้การนับการอ้างอิง) JavaScriptและECMAScriptก็มีแนวโน้มที่จะใช้ GC เช่นกัน ภาษา การเขียนโปรแกรมเชิงวัตถุเช่นSmalltalk , ooRexx , RPLและJavaมักจะมีระบบเก็บขยะในตัว ข้อยกเว้นที่สำคัญคือC++และDelphiซึ่งมีตัวทำลาย
พื้นฐาน
BASICและLogoมักใช้การเก็บขยะสำหรับชนิดข้อมูลที่มีความยาวแปรผัน เช่น สตริงและลิสต์ เพื่อไม่ให้โปรแกรมเมอร์ต้องแบกรับภาระกับรายละเอียดการจัดการหน่วยความจำ บนAltair 8800โปรแกรมที่มีตัวแปรสตริงจำนวนมากและพื้นที่สตริงน้อยอาจทำให้เกิดการหยุดชั่วคราวเป็นเวลานานเนื่องจากการเก็บขยะ[ 25 ]ในทำนองเดียวกัน อัลกอริทึมการเก็บขยะของตัวแปลภาษา Applesoft BASICจะสแกนตัวอธิบายสตริงซ้ำๆ เพื่อหาสตริงที่มีที่อยู่สูงสุดเพื่อบีบอัดไปยังหน่วยความจำส่วนบน ส่งผลให้ประสิทธิภาพ ลดลง [ 26 ]และหยุดชั่วคราวตั้งแต่ไม่กี่วินาทีถึงไม่กี่นาที[ 27 ]ตัวเก็บขยะทดแทนสำหรับ Applesoft BASIC โดยRandy Wiggintonจะระบุกลุ่มของสตริงในทุกรอบของฮีป ลดเวลาการเก็บขยะลงอย่างมาก[ 28 ] BASIC.SYSTEM ซึ่งเปิดตัวพร้อมกับProDOSในปี 1983 มีตัวเก็บขยะแบบหน้าต่างสำหรับ BASIC ที่เร็วกว่าหลายเท่า[ 29 ]
ซี และ ซี++
ภาษา Cไม่เคยให้การสนับสนุนอย่างเป็นทางการสำหรับการเก็บขยะภาษา C++เพิ่มการสนับสนุนการเก็บขยะในไลบรารีมาตรฐาน ใน C++11 แต่ถูกลบออกใน C++23เนื่องจากไม่มีคอมไพเลอร์ใดที่รองรับฟีเจอร์นี้[ 30 ]ฟีเจอร์ที่เป็นส่วนหนึ่งของสิ่งนี้เกี่ยวข้องกับความปลอดภัยของตัวชี้[ 31 ]
แม้ว่าการสนับสนุนการจัดการขยะในไลบรารีมาตรฐานจะถูกลบออกไปแล้ว แต่ตัวจัดการขยะบางตัว เช่นตัวจัดการขยะ Boehm (สำหรับ C และ C++) ยังคงสามารถใช้งานได้ ตัวจัดการขยะ Boehm ใช้การจัดการขยะแบบ mark-and-sweepนอกจากนี้ยังสามารถใช้งานในโหมดตรวจจับการรั่วไหลได้ ซึ่งการจัดการหน่วยความจำยังคงเป็นแบบแมนนวล แต่สามารถตรวจจับและรายงานการรั่วไหลและข้อผิดพลาด double-free ได้ สามารถเรียกใช้งานจากไฟล์เฮดเดอร์<gc.h>ได้
เรายังสามารถกำจัดปัญหาการทำลายอ็อบเจ็กต์ด้วยตนเองใน C++ ได้โดยใช้แนวคิด " การได้มาซึ่งทรัพยากรคือการเริ่มต้น " (RAII) และสมาร์ทพอยเตอร์ RAII std::unique_ptrผูกอายุการใช้งานเข้ากับการเป็นเจ้าของ ในขณะที่ RAII std::shared_ptrใช้การนับการอ้างอิงเพื่อกำหนดอายุการใช้งานstd::weak_ptrRAII สามารถใช้เพื่อรับพอยเตอร์โดยไม่ต้องเพิ่มจำนวนการอ้างอิง ต่างจากการเก็บขยะ RAII เป็นแบบกำหนดได้แน่นอน
ออบเจกทีฟซี
แม้ว่าObjective-Cจะไม่มีการเก็บขยะ (garbage collection) ตามธรรมเนียม แต่เมื่อมีการเปิดตัวOS X 10.5ในปี 2007 Appleได้นำการเก็บขยะมาใช้กับObjective-C 2.0 โดยใช้ตัวเก็บรวบรวมรันไทม์ที่พัฒนาขึ้นเอง[ 32 ] อย่างไรก็ตาม เมื่อมีการเปิดตัวOS X 10.8 ในปี 2012 การเก็บขยะถูกยกเลิกและแทนที่ด้วยตัวนับการอ้างอิงอัตโนมัติ (ARC) ของLLVMที่เปิดตัวพร้อมกับOS X 10.7 [ 33 ] นอกจากนี้ ตั้งแต่เดือนพฤษภาคม 2015 Apple ยังห้ามการใช้การเก็บขยะสำหรับแอปพลิเคชัน OS X ใหม่ในApp Store อีกด้วย [ 34 ] [ 35 ]สำหรับiOSการเก็บขยะไม่เคยถูกนำมาใช้เนื่องจากปัญหาในการตอบสนองและประสิทธิภาพของแอปพลิเคชัน[ 13 ] [ 36 ]แต่ iOS ใช้ ARC แทน[ 37 ] [ 38 ]
สภาพแวดล้อมที่จำกัด
การเก็บขยะมักไม่ค่อยได้ใช้ใน ระบบ ฝังตัวหรือระบบเรียลไทม์ เนื่องจากโดยปกติแล้วจำเป็นต้องควบคุมการใช้ทรัพยากรที่มีจำกัดอย่างเข้มงวดมาก อย่างไรก็ตาม ได้มีการพัฒนาระบบเก็บขยะที่เข้ากันได้กับสภาพแวดล้อมที่มีข้อจำกัดหลายอย่าง[ 39 ] Microsoft .NET Micro Framework , .NET nanoFramework [ 40 ]และJava Platform, Micro Editionเป็นแพลตฟอร์มซอฟต์แวร์ฝังตัว ซึ่งเช่นเดียวกับแพลตฟอร์มขนาดใหญ่กว่า ก็มีการเก็บขยะรวมอยู่ด้วย
ชวา
ตัวจัดการขยะ (Garbage collector) ที่มีอยู่ในเครื่องเสมือน (JVM) ของ Java OpenJDK ได้แก่:
- ซีเรียล
- ขนาน
- CMS (Concurrent Mark Sweep)
- G1 (ระบบจัดการขยะแบบเริ่มต้นก่อน)
- ZGC (ตัวเก็บขยะ Z)
- เอปซิลอน
- เชนันโดอาห์
- GenZGC (Generational ZGC)
- เก็นเชิน (เชนนันโดอาห์รุ่นต่อรุ่น)
- เครื่องจับจังหวะของ IBM (มีเฉพาะในIBM OpenJDK)
- SAP (เฉพาะในSAP OpenJDK)
- Azul C4 (Continuously Concurrent Compacting Collector) [ 41 ] (เฉพาะในAzul Systems OpenJDK)
การใช้งานระหว่างการคอมไพล์
การจัดการหน่วยความจำอัตโนมัติในระหว่างการคอมไพล์ (Compile-time garbage collection) เป็นรูปแบบหนึ่งของการวิเคราะห์แบบคงที่ (Static analysis)ที่อนุญาตให้ใช้หน่วยความจำซ้ำและเรียกคืนหน่วยความจำได้โดยอิงตามเงื่อนไขที่ทราบในระหว่างการคอมไพล์
รูปแบบการเก็บขยะนี้ได้รับการศึกษาใน ภาษาการ เขียนโปรแกรม Mercury [ 42 ]และมีการใช้งานมากขึ้นเมื่อมีการนำตัวนับการอ้างอิงอัตโนมัติ (ARC) ของLLVMเข้าสู่ระบบนิเวศของ Apple (iOS และ OS X) ในปี 2011 [ 37 ] [ 38 ] [ 34 ]
ระบบเรียลไทม์
ตัวเก็บขยะแบบเพิ่มทีละน้อย แบบพร้อมกัน และแบบเรียลไทม์ได้รับการพัฒนาขึ้น ตัวอย่างเช่น โดยHenry BakerและHenry Lieberman [ 43 ] [ 44 ] [ 45 ]
ในอัลกอริทึมของเบเกอร์ การจัดสรรจะทำในครึ่งใดครึ่งหนึ่งของพื้นที่หน่วยความจำเดียว เมื่อพื้นที่เต็มครึ่งหนึ่ง จะมีการดำเนินการเก็บขยะ ซึ่งจะย้ายวัตถุที่ยังใช้งานอยู่ไปยังอีกครึ่งหนึ่ง และวัตถุที่เหลือจะถูกยกเลิกการจัดสรรโดยปริยาย โปรแกรมที่กำลังทำงานอยู่ ('ตัวแก้ไข') จะต้องตรวจสอบว่าวัตถุใด ๆ ที่อ้างอิงอยู่ในครึ่งที่ถูกต้องหรือไม่ และหากไม่อยู่ในครึ่งที่ถูกต้อง ให้ย้ายไปอีกครึ่ง ในขณะที่งานเบื้องหลังกำลังค้นหาวัตถุทั้งหมด[ 46 ]
ระบบ การเก็บขยะแบบแบ่งตามรุ่นอายุ (Generational garbage collection) อิงตามข้อสังเกตเชิงประจักษ์ที่ว่าวัตถุส่วนใหญ่มีอายุสั้น ในระบบนี้ จะมีการจัดเก็บพื้นที่จัดสรร (รุ่นอายุ) สองพื้นที่ขึ้นไป ซึ่งแยกออกจากกันตามอายุของวัตถุ วัตถุใหม่จะถูกสร้างขึ้นในรุ่นอายุ "ใหม่" ซึ่งจะถูกเก็บขยะเป็นประจำ และเมื่อรุ่นอายุหนึ่งเต็ม วัตถุที่ยังคงถูกอ้างอิงจากพื้นที่เก่ากว่าจะถูกคัดลอกไปยังรุ่นอายุถัดไปที่เก่ากว่า บางครั้งอาจมีการสแกนพื้นที่ทั้งหมด (full scan) ด้วย
สถาปัตยกรรมคอมพิวเตอร์สำหรับภาษาโปรแกรมระดับสูงบางแบบมีการรองรับฮาร์ดแวร์สำหรับการจัดการหน่วยความจำแบบเรียลไทม์
การใช้งานตัวเก็บขยะแบบเรียลไทม์ส่วนใหญ่ใช้การติดตามตัวเก็บขยะแบบเรียลไทม์ดังกล่าวตรงตาม ข้อจำกัด แบบเรียลไทม์ที่เข้มงวดเมื่อใช้กับระบบปฏิบัติการแบบเรียลไทม์[ 47 ]
ดูเพิ่มเติม
- ตัวทำลาย (การเขียนโปรแกรมคอมพิวเตอร์)
- การกำจัดโค้ดที่ไม่ได้ใช้งานแบบไดนามิก
- ตัวชี้อัจฉริยะ
- การบีบอัดหน่วยความจำเสมือน
- การเก็บขยะ (SSD)
- การเก็บขยะโดยใช้ฮาร์ดแวร์ช่วย
อ่านเพิ่มเติม
- Jones, Richard; Hosking, Antony; Moss, J. Eliot B. (16 สิงหาคม 2554). คู่มือการจัดการหน่วยความจำอัตโนมัติ: ศิลปะแห่งการจัดการหน่วยความจำอัตโนมัติชุด CRC Applied Algorithms and Data Structures. Chapman and Hall / CRC Press / Taylor & Francis Ltd. ISBN 978-1-4200-8279-1.(511 หน้า)
- โจนส์, ริชาร์ด; ลินส์, ราฟาเอล (12 กรกฎาคม 1996). การเก็บขยะ: อัลกอริทึมสำหรับการจัดการหน่วยความจำแบบไดนามิกอัตโนมัติ (ฉบับที่ 1). ไวลีย์ . ISBN 978-0-47194148-4.(404 หน้า)
- Schorr, Herbert; Waite, William M. (สิงหาคม 1967). "ขั้นตอนที่มีประสิทธิภาพที่ไม่ขึ้นกับเครื่องจักรสำหรับการเก็บขยะในโครงสร้างรายการต่างๆ" (PDF) . Communications of the ACM . 10 (8): 501– 506. doi : 10.1145/363534.363554 . S2CID 5684388 . เก็บถาวร(PDF)จากต้นฉบับเมื่อ 2021-01-22.
- วิลสัน, พอล อาร์. ( 1992). " เทคนิคการเก็บขยะของโปรเซสเซอร์เดี่ยว" การจัดการหน่วยความจำบันทึกการบรรยายในวิทยาการคอมพิวเตอร์ เล่มที่ 637 สปริงเกอร์-เวอร์แลกหน้า 1–42 CiteSeerX 10.1.1.47.2438 doi : 10.1007/bfb0017182 ISBN 3-540-55940-X.
{{cite book}}:|journal=ละเลย ( ช่วยเหลือ ) - วิลสัน, พอล อาร์.; จอห์นสโตน, มาร์ค เอส.; นีลี, ไมเคิล; โบเลส, เดวิด (1995). "การจัดสรรพื้นที่จัดเก็บข้อมูลแบบไดนามิก: การสำรวจและการวิเคราะห์เชิงวิพากษ์" การจัดการหน่วยความจำบันทึกการบรรยายในวิทยาการคอมพิวเตอร์ เล่มที่ 986 (ฉบับที่ 1). หน้า 1–116 . CiteSeerX 10.1.1.47.275 . doi : 10.1007/3-540-60368-9_19 . ISBN 978-3-540-60368-9.
{{cite book}}:|journal=ละเลย ( ช่วยเหลือ )
ลิงก์ภายนอก
- เอกสารอ้างอิงการจัดการหน่วยความจำถูกเก็บถาวรเมื่อวันที่ 13 ธันวาคม 2020 ที่Wayback Machine
- หลักการพื้นฐานที่สุดของการเก็บขยะ
- การปรับแต่งการจัดการขยะของเครื่องเสมือน HotSpot ใน Java SE 6
- TinyGC - การใช้งาน API ของ BoehmGC อย่างอิสระ
- การนำระบบเก็บขยะแบบอนุรักษ์นิยมมาใช้ในภาษาซี
- MeixnerGC - ตัวเก็บขยะแบบเพิ่มทีละขั้น (incremental mark and sweep) สำหรับ C++ ที่ใช้ตัวชี้อัจฉริยะ (smart pointers)