กลไกการรวบรวมขยะของแพลตฟอร์ม Java ได้ปรับปรุงประสิทธิภาพของนักพัฒนาอย่างมีนัยสำคัญ แต่ตัวรวบรวมขยะที่ใช้งานไม่ดีอาจใช้ทรัพยากรแอปพลิเคชันมากเกินไป ในส่วนที่สามของซีรีส์การเพิ่มประสิทธิภาพเครื่องเสมือน Java Eva Andreasson แนะนำผู้เริ่มต้น Java ให้รู้จักกับโมเดลหน่วยความจำและกลไกการรวบรวมขยะของแพลตฟอร์ม Java เธออธิบายว่าเหตุใดการกระจายตัว (ไม่ใช่การรวบรวมขยะ) จึงเป็นปัญหาหลักในประสิทธิภาพของแอปพลิเคชัน Java และเหตุใดการรวบรวมและการบดอัดขยะทั่วไปจึงเป็นวิธีหลัก (แต่ไม่ใช่นวัตกรรมใหม่ที่สุด) ในการจัดการกับการกระจายตัวของแอปพลิเคชัน Java
วัตถุประสงค์ของการรวบรวมขยะ (GC) คือการปล่อยหน่วยความจำที่ถูกครอบครองโดยอ็อบเจ็กต์ Java ที่ไม่ได้อ้างอิงโดยอ็อบเจ็กต์ที่ใช้งานอยู่อีกต่อไป มันเป็นส่วนหลักของกลไกการจัดการหน่วยความจำแบบไดนามิกของเครื่องเสมือน Java ในระหว่างรอบการรวบรวมขยะทั่วไป ออบเจ็กต์ทั้งหมดที่ยังคงอ้างอิงอยู่ (และสามารถเข้าถึงได้) จะถูกเก็บรักษาไว้ ในขณะที่ออบเจ็กต์ที่ไม่มีการอ้างอิงอีกต่อไปจะถูกปล่อยออกมา และพื้นที่ที่พวกมันครอบครองจะถูกเรียกคืนเพื่อการจัดสรรให้กับออบเจ็กต์ใหม่
เพื่อทำความเข้าใจกลไกการรวบรวมขยะและอัลกอริธึมการรวบรวมขยะต่างๆ คุณจำเป็นต้องรู้บางอย่างเกี่ยวกับโมเดลหน่วยความจำแพลตฟอร์ม Java ก่อน
การรวบรวมขยะและโมเดลหน่วยความจำแพลตฟอร์ม Java
เมื่อคุณเริ่มโปรแกรม Java จากบรรทัดคำสั่งและระบุพารามิเตอร์เริ่มต้น -Xmx (เช่น: java -Xmx:2g MyApp) หน่วยความจำตามขนาดที่ระบุจะถูกจัดสรรให้กับกระบวนการ Java ซึ่งเรียกว่า Java heap . พื้นที่ที่อยู่หน่วยความจำเฉพาะนี้ใช้เพื่อจัดเก็บอ็อบเจ็กต์ที่สร้างโดยโปรแกรม Java (และบางครั้งก็เป็น JVM) ในขณะที่แอปพลิเคชันทำงานและจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ใหม่อย่างต่อเนื่อง ฮีป Java (นั่นคือ พื้นที่ที่อยู่หน่วยความจำเฉพาะ) จะค่อยๆ เต็ม
ในที่สุดฮีป Java จะเต็ม ซึ่งหมายความว่าเธรดการจัดสรรหน่วยความจำไม่สามารถค้นหาพื้นที่ต่อเนื่องกันที่มีขนาดใหญ่พอที่จะจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ใหม่ ในเวลานี้ JVM ตัดสินใจแจ้งตัวรวบรวมขยะและเริ่มการรวบรวมขยะ การรวบรวมขยะสามารถถูกกระตุ้นได้โดยการเรียก System.gc() ในโปรแกรม แต่การใช้ System.gc() ไม่ได้รับประกันว่าการรวบรวมขยะจะดำเนินการ ก่อนการรวบรวมขยะ กลไกการรวบรวมขยะจะกำหนดก่อนว่าจะปลอดภัยหรือไม่ที่จะดำเนินการรวบรวมขยะ เมื่อเธรดที่ใช้งานอยู่ทั้งหมดของแอปพลิเคชันอยู่ในจุดที่ปลอดภัย การรวบรวมขยะก็สามารถเริ่มต้นได้ ตัวอย่างเช่น การรวบรวมขยะไม่สามารถทำได้เมื่อมีการจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ หรือการรวบรวมขยะไม่สามารถทำได้ในขณะที่คำสั่ง CPU กำลังปรับให้เหมาะสม เนื่องจากบริบทมีแนวโน้มที่จะสูญหายและผลลัพธ์สุดท้ายจะไม่ถูกต้อง
ตัวรวบรวมขยะไม่สามารถเรียกคืนออบเจ็กต์ใด ๆ ที่มีการอ้างอิงที่ใช้งานอยู่ ซึ่งจะทำให้ข้อกำหนดเฉพาะของเครื่องเสมือน Java เสียหาย ไม่จำเป็นต้องรีไซเคิลวัตถุที่ตายแล้วทันที เพราะในที่สุดแล้ววัตถุที่ตายแล้วก็จะถูกรีไซเคิลด้วยการรวบรวมขยะในภายหลัง แม้ว่าจะมีหลายวิธีในการดำเนินการรวบรวมขยะ แต่สองประเด็นข้างต้นจะเหมือนกันสำหรับการดำเนินการรวบรวมขยะทั้งหมด ความท้าทายที่แท้จริงของการรวบรวมขยะคือการระบุว่าวัตถุยังมีชีวิตอยู่หรือไม่ และวิธีเรียกคืนหน่วยความจำโดยไม่ส่งผลกระทบต่อแอปพลิเคชันให้มากที่สุด ดังนั้น ตัวรวบรวมขยะจึงมีเป้าหมายสองประการดังต่อไปนี้:
1. ปล่อยหน่วยความจำที่ไม่ได้อ้างอิงอย่างรวดเร็วเพื่อให้ตรงกับความต้องการในการจัดสรรหน่วยความจำของแอปพลิเคชัน เพื่อหลีกเลี่ยงหน่วยความจำล้น
2. ลดผลกระทบต่อการรันประสิทธิภาพของแอปพลิเคชัน (เวลาแฝงและปริมาณงาน) เมื่อเรียกคืนหน่วยความจำ
การเก็บขยะสองประเภท
ในบทความแรกของชุดนี้ ฉันได้แนะนำวิธีการรวบรวมขยะสองวิธี ได้แก่ การนับการอ้างอิงและการติดตามการรวบรวม ต่อไป เราจะสำรวจแนวทางทั้งสองนี้เพิ่มเติม และแนะนำอัลกอริธึมการรวบรวมการติดตามบางส่วนที่ใช้ในสภาพแวดล้อมการผลิต
ตัวสะสมการนับการอ้างอิง
ตัวรวบรวมการนับการอ้างอิงจะบันทึกจำนวนการอ้างอิงที่ชี้ไปยังแต่ละอ็อบเจ็กต์ Java เมื่อจำนวนการอ้างอิงที่ชี้ไปยังอ็อบเจ็กต์ถึง 0 จะสามารถรีไซเคิลอ็อบเจ็กต์ได้ทันที ความเร่งด่วนนี้เป็นข้อได้เปรียบหลักของตัวรวบรวมการนับการอ้างอิง และแทบไม่มีค่าใช้จ่ายใดๆ ในการรักษาหน่วยความจำที่ไม่มีการอ้างอิงชี้ไป แต่การติดตามจำนวนการอ้างอิงล่าสุดสำหรับแต่ละอ็อบเจ็กต์มีราคาแพง
ปัญหาหลักของตัวรวบรวมการนับการอ้างอิงคือวิธีการตรวจสอบความถูกต้องของการนับอ้างอิง ปัญหาที่ทราบกันดีอีกประการหนึ่งคือวิธีจัดการกับการอ้างอิงแบบวงกลม ถ้าวัตถุสองชิ้นอ้างอิงถึงกันและกันและไม่ได้อ้างอิงโดยวัตถุมีชีวิตอื่น หน่วยความจำของวัตถุทั้งสองจะไม่ถูกเรียกคืนเนื่องจากจำนวนการอ้างอิงที่ชี้ไปยังวัตถุทั้งสองเป็น 0 การรีไซเคิลหน่วยความจำของโครงสร้างอ้างอิงแบบวงกลมจำเป็นต้องมีการวิเคราะห์ที่สำคัญ (หมายเหตุผู้แปล: การวิเคราะห์ทั่วโลกบน Java Heap) ซึ่งจะเพิ่มความซับซ้อนของอัลกอริทึมและนำค่าใช้จ่ายเพิ่มเติมมาสู่แอปพลิเคชัน
นักสะสมร่องรอย
ตัวรวบรวมการติดตามขึ้นอยู่กับสมมติฐานที่ว่าวัตถุที่มีชีวิตทั้งหมดสามารถพบได้โดยการวนซ้ำการอ้างอิง (การอ้างอิงและการอ้างอิงของการอ้างอิง) ไปยังชุดเริ่มต้นของวัตถุที่มีชีวิตที่รู้จัก ชุดเริ่มต้นของอ็อบเจ็กต์ที่ใช้งานอยู่ (หรือเรียกว่าอ็อบเจ็กต์รูท) สามารถกำหนดได้โดยการวิเคราะห์รีจิสเตอร์ อ็อบเจ็กต์โกลบอล และเฟรมสแต็ก หลังจากกำหนดชุดเริ่มต้นของออบเจ็กต์แล้ว ตัวรวบรวมการติดตามจะติดตามความสัมพันธ์การอ้างอิงของออบเจ็กต์เหล่านี้ และทำเครื่องหมายออบเจ็กต์ที่อ้างอิงถึงเป็นออบเจ็กต์ที่ใช้งานอยู่ตามลำดับ เพื่อให้ชุดของออบเจ็กต์ที่ใช้งานอยู่ที่รู้จักยังคงขยายต่อไป กระบวนการนี้จะดำเนินต่อไปจนกว่าวัตถุที่อ้างอิงทั้งหมดจะถูกทำเครื่องหมายเป็นวัตถุมีชีวิต และหน่วยความจำของวัตถุเหล่านั้นที่ไม่ได้ถูกทำเครื่องหมายจะถูกเรียกคืน
ตัวรวบรวมการติดตามแตกต่างจากตัวรวบรวมการนับอ้างอิงโดยหลักตรงที่สามารถจัดการโครงสร้างอ้างอิงแบบวงกลมได้ ตัวรวบรวมการติดตามส่วนใหญ่จะค้นพบวัตถุที่ไม่ได้อ้างอิงในโครงสร้างอ้างอิงแบบวงกลมในระหว่างขั้นตอนการทำเครื่องหมาย
ตัวรวบรวมการติดตามเป็นวิธีการจัดการหน่วยความจำที่ใช้กันมากที่สุดในภาษาไดนามิกและปัจจุบันเป็นวิธีที่ใช้กันทั่วไปที่สุดใน Java นอกจากนี้ยังได้รับการตรวจสอบในสภาพแวดล้อมการใช้งานจริงเป็นเวลาหลายปี ด้านล่างนี้ ผมจะแนะนำตัวรวบรวมการติดตามโดยเริ่มจากอัลกอริธึมบางตัวสำหรับการนำการรวบรวมการติดตามไปใช้
อัลกอริธึมการรวบรวมการติดตาม
การคัดลอกตัวรวบรวมขยะและผู้รวบรวมขยะแบบกวาดล้างนั้นไม่ใช่เรื่องใหม่ แต่ยังคงเป็นอัลกอริธึมสองวิธีที่พบบ่อยที่สุดสำหรับการนำการติดตามคอลเลกชั่นไปใช้ในปัจจุบัน
คัดลอกคนเก็บขยะ
ตัวรวบรวมขยะการคัดลอกแบบดั้งเดิมใช้ช่องว่างที่อยู่สองช่องในฮีป (เช่น จากช่องว่างและไปยังช่องว่าง) เมื่อดำเนินการรวบรวมขยะ ออบเจ็กต์ที่ใช้งานอยู่ในพื้นที่จากจะถูกคัดลอกไปยังพื้นที่ถึง เมื่อวัตถุที่ใช้งานอยู่ในพื้นที่จาก จะถูกลบออก (หมายเหตุของผู้แปล: หลังจากคัดลอกไปยังพื้นที่หรือรุ่นเก่า) ทั้งหมดจากอวกาศสามารถรีไซเคิลได้ เมื่อมีการจัดสรรพื้นที่อีกครั้ง พื้นที่ถึงจะถูกใช้ก่อน (หมายเหตุของผู้แปล: นั่นคือ พื้นที่ถึง ของรอบที่แล้วจะถูกใช้เป็นพื้นที่รอบใหม่)
ในการใช้งานอัลกอริธึมนี้ในช่วงแรก จาก space และ to space จะเปลี่ยนตำแหน่งอย่างต่อเนื่อง กล่าวคือ เมื่อ to space เต็มและมีการทริกเกอร์การรวบรวมขยะ to space จะกลายเป็นจากอวกาศ ดังแสดงในรูปที่ 1 .
รูปที่ 1 ลำดับการรวบรวมขยะสำเนาแบบดั้งเดิม
อัลกอริธึมการคัดลอกล่าสุดช่วยให้พื้นที่ที่อยู่ใดๆ ในฮีปสามารถใช้เป็นพื้นที่และจากพื้นที่ได้ วิธีนี้ทำให้พวกเขาไม่จำเป็นต้องสลับตำแหน่งระหว่างกัน แต่เพียงแค่เปลี่ยนตำแหน่งตามตรรกะเท่านั้น
ข้อดีของตัวรวบรวมการคัดลอกคือออบเจ็กต์ที่คัดลอกในพื้นที่ถึงจะถูกจัดเรียงอย่างแน่นหนา และไม่มีการกระจายตัวเลย การแยกส่วนเป็นปัญหาทั่วไปที่คนเก็บขยะคนอื่นๆ เผชิญ และยังเป็นประเด็นหลักที่เราจะหารือในภายหลัง
ข้อเสียของตัวสะสมสำเนา
โดยทั่วไปแล้ว ตัวรวบรวมการคัดลอกจะหยุดโลก ซึ่งหมายความว่าตราบใดที่การรวบรวมขยะยังดำเนินการอยู่ แอปพลิเคชันจะไม่สามารถดำเนินการได้ ด้วยการใช้งานนี้ ยิ่งคุณต้องคัดลอกสิ่งต่าง ๆ มากเท่าใด ผลกระทบต่อประสิทธิภาพของแอปพลิเคชันก็จะยิ่งมากขึ้นเท่านั้น นี่เป็นข้อเสียสำหรับแอปพลิเคชันที่ไวต่อเวลาตอบสนอง เมื่อใช้ตัวรวบรวมสำเนา คุณยังต้องพิจารณาสถานการณ์ที่เลวร้ายที่สุด (นั่นคือ วัตถุทั้งหมดในพื้นที่จากเป็นวัตถุที่ใช้งานอยู่) ในเวลานี้ คุณจำเป็นต้องเตรียมพื้นที่ขนาดใหญ่เพียงพอสำหรับการย้ายวัตถุที่ใช้งานอยู่เหล่านี้ ดังนั้นถึง พื้นที่จะต้องมีขนาดใหญ่พอที่จะติดตั้งวัตถุทั้งหมดในพื้นที่จาก เนื่องจากข้อจำกัดนี้ การใช้หน่วยความจำของอัลกอริธึมการคัดลอกจึงไม่เพียงพอเล็กน้อย (หมายเหตุผู้แปล: ในกรณีที่แย่ที่สุด พื้นที่ to จะต้องมีขนาดเท่ากันกับพื้นที่จาก ดังนั้นการใช้งานเพียง 50% เท่านั้น)
นักสะสมที่ชัดเจน
JVM เชิงพาณิชย์ส่วนใหญ่ปรับใช้ในสภาพแวดล้อมการผลิตระดับองค์กรใช้ตัวรวบรวมแบบกวาดล้าง (หรือทำเครื่องหมาย) เนื่องจากไม่ได้จำลองผลกระทบของตัวรวบรวมขยะต่อประสิทธิภาพของแอปพลิเคชัน นักสะสมเครื่องหมายที่มีชื่อเสียงที่สุด ได้แก่ CMS, G1, GenPar และ DetermisticGC
ตัวรวบรวมการกวาดล้างติดตามการอ้างอิงวัตถุและทำเครื่องหมายแต่ละวัตถุที่พบว่าใช้งานได้จริงโดยใช้บิตแฟล็ก แฟล็กนี้มักจะสอดคล้องกับที่อยู่หรือกลุ่มที่อยู่ในฮีป ตัวอย่างเช่น: บิตที่ใช้งานอาจเป็นบิตในส่วนหัวของวัตถุ (หมายเหตุผู้แปล: บิต) หรือบิตเวกเตอร์หรือบิตแมป
หลังจากการทำเครื่องหมายเสร็จสิ้น จะเข้าสู่ขั้นตอนการล้างข้อมูล ขั้นตอนการล้างข้อมูลมักจะสำรวจฮีปอีกครั้ง (ไม่ใช่แค่ออบเจ็กต์ที่ถูกทำเครื่องหมายว่าใช้งานได้ แต่รวมถึงฮีปทั้งหมด) เพื่อค้นหาพื้นที่ที่อยู่หน่วยความจำที่ต่อเนื่องกันที่ไม่ได้ทำเครื่องหมาย (หน่วยความจำที่ไม่ได้ทำเครื่องหมายนั้นว่างและรีไซเคิลได้) จากนั้นตัวรวบรวมจะจัดระเบียบพวกมันให้เป็นรายการว่าง ตัวรวบรวมขยะสามารถมีรายการว่างได้หลายรายการ (โดยปกติจะแบ่งตามขนาดของบล็อกหน่วยความจำ) ตัวรวบรวม JVM บางตัว (เช่น JRockit Real Time) ยังสามารถแบ่งรายการว่างแบบไดนามิกตามการวิเคราะห์ประสิทธิภาพของแอปพลิเคชันและสถิติขนาดอ็อบเจ็กต์
หลังจากขั้นตอนการล้างข้อมูล แอปพลิเคชันสามารถจัดสรรหน่วยความจำได้อีกครั้ง เมื่อจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ใหม่จากรายการว่าง บล็อกหน่วยความจำที่จัดสรรใหม่จะต้องพอดีกับขนาดของอ็อบเจ็กต์ใหม่ หรือขนาดอ็อบเจ็กต์เฉลี่ยของเธรด หรือขนาด TLAB ของแอปพลิเคชัน การค้นหาบล็อกหน่วยความจำที่มีขนาดเหมาะสมสำหรับอ็อบเจ็กต์ใหม่จะช่วยปรับหน่วยความจำให้เหมาะสมและลดการกระจายตัว
มาร์ค - ล้างข้อบกพร่องของนักสะสม
เวลาดำเนินการของเฟสการทำเครื่องหมายขึ้นอยู่กับจำนวนของอ็อบเจ็กต์ที่ใช้งานอยู่ในฮีป ในขณะที่เวลาดำเนินการของเฟสการล้างข้อมูลขึ้นอยู่กับขนาดของฮีป ดังนั้น สำหรับสถานการณ์ที่การตั้งค่าฮีปมีขนาดใหญ่และมีออบเจ็กต์ที่ใช้งานอยู่จำนวนมากในฮีป อัลกอริธึม mark-sweep จะมีเวลาหยุดชั่วคราว
สำหรับแอปพลิเคชันที่ใช้หน่วยความจำมาก คุณสามารถปรับพารามิเตอร์การรวบรวมขยะให้เหมาะสมกับสถานการณ์และความต้องการของแอปพลิเคชันต่างๆ ในหลายกรณี การปรับเปลี่ยนนี้อย่างน้อยจะเลื่อนความเสี่ยงที่เกิดจากขั้นตอนการทำเครื่องหมาย/กวาดล้างไปยังแอปพลิเคชันหรือข้อตกลงการบริการ SLA (SLA ในที่นี้หมายถึงเวลาตอบสนองที่แอปพลิเคชันจำเป็นต้องบรรลุ) แต่การปรับแต่งจะมีผลเฉพาะกับโหลดเฉพาะและอัตราการจัดสรรหน่วยความจำเท่านั้น การเปลี่ยนแปลงโหลดหรือการปรับเปลี่ยนแอปพลิเคชันนั้นจำเป็นต้องมีการปรับแต่งใหม่
การดำเนินการรวบรวมเครื่องหมายกวาด
มีวิธีการอย่างน้อยสองวิธีที่ได้รับการพิสูจน์แล้วในเชิงพาณิชย์สำหรับการดำเนินการรวบรวมขยะแบบ Mark-sweep หนึ่งคือการรวบรวมขยะแบบขนาน และอีกอันคือการรวบรวมขยะที่เกิดขึ้นพร้อมกัน (หรือส่วนใหญ่เกิดขึ้นพร้อมกัน)
ตัวสะสมแบบขนาน
การรวบรวมแบบขนานหมายความว่าทรัพยากรถูกใช้แบบขนานโดยเธรดการรวบรวมขยะ การใช้งานการรวบรวมแบบขนานในเชิงพาณิชย์ส่วนใหญ่เป็นตัวรวบรวมแบบหยุดโลก ซึ่งเธรดแอปพลิเคชันทั้งหมดจะถูกหยุดชั่วคราวจนกว่าการรวบรวมขยะจะเสร็จสมบูรณ์ เนื่องจากตัวรวบรวมขยะสามารถใช้ทรัพยากรได้อย่างมีประสิทธิภาพ จึงมักจะทำงานได้ดีกว่าในเกณฑ์มาตรฐานการรับส่งข้อมูล SPECjbb. หากปริมาณงานมีความสำคัญต่อแอปพลิเคชันของคุณ เครื่องรวบรวมขยะแบบขนานก็เป็นตัวเลือกที่ดี
ต้นทุนหลักของการรวบรวมแบบขนาน (โดยเฉพาะสำหรับสภาพแวดล้อมการใช้งานจริง) คือเธรดของแอปพลิเคชันไม่สามารถทำงานได้อย่างถูกต้องในระหว่างการรวบรวมขยะ เช่นเดียวกับตัวรวบรวมการคัดลอก ดังนั้น การใช้ตัวรวบรวมแบบขนานจะมีผลกระทบอย่างมากต่อแอปพลิเคชันที่ไวต่อเวลาตอบสนอง โดยเฉพาะอย่างยิ่งเมื่อมีโครงสร้างอ็อบเจ็กต์ที่ใช้งานที่ซับซ้อนจำนวนมากในพื้นที่ฮีป มีการอ้างอิงอ็อบเจ็กต์จำนวนมากที่จำเป็นต้องติดตาม (โปรดจำไว้ว่าเวลาที่ใช้สำหรับ mark-sweep collector เพื่อเรียกคืนหน่วยความจำนั้นขึ้นอยู่กับเวลาที่ใช้ในการติดตามคอลเลกชันของอ็อบเจ็กต์ที่มีชีวิต บวกกับเวลาที่ใช้ในการสำรวจฮีปทั้งหมด) ด้วยวิธีการแบบขนาน แอปพลิเคชันจะหยุดชั่วคราวสำหรับ เวลาเก็บขยะทั้งหมด
นักสะสมพร้อมกัน
ตัวรวบรวมขยะพร้อมกันเหมาะสำหรับแอปพลิเคชันที่ไวต่อเวลาตอบสนองมากกว่า การทำงานพร้อมกันหมายความว่าเธรดการรวบรวมขยะและเธรดแอปพลิเคชันดำเนินการพร้อมกัน เธรดการรวบรวมขยะไม่ได้เป็นเจ้าของทรัพยากรทั้งหมด ดังนั้นจึงจำเป็นต้องตัดสินใจว่าจะเริ่มการรวบรวมขยะเมื่อใด เพื่อให้มีเวลาเพียงพอในการติดตามการรวบรวมอ็อบเจ็กต์ที่ใช้งานอยู่และเรียกคืนหน่วยความจำก่อนที่หน่วยความจำแอปพลิเคชันจะล้น หากการรวบรวมขยะไม่เสร็จสมบูรณ์ทันเวลา แอปพลิเคชันจะทำให้เกิดข้อผิดพลาดหน่วยความจำล้น ในทางกลับกัน คุณไม่ต้องการให้การรวบรวมขยะใช้เวลานานเกินไป เนื่องจากจะใช้ทรัพยากรของแอปพลิเคชันและส่งผลต่อปริมาณงาน การรักษาสมดุลนี้ต้องใช้ทักษะ ดังนั้นจึงใช้การวิเคราะห์พฤติกรรมเพื่อกำหนดเวลาที่จะเริ่มการรวบรวมขยะ และเมื่อใดควรเลือกการเพิ่มประสิทธิภาพการรวบรวมขยะ
ปัญหาอีกประการหนึ่งคือการพิจารณาว่าเมื่อใดจึงปลอดภัยที่จะดำเนินการบางอย่าง (การดำเนินการที่ต้องใช้สแน็ปช็อตฮีปที่สมบูรณ์และแม่นยำ) เช่น การจำเป็นต้องรู้ว่าเมื่อใดที่เฟสการทำเครื่องหมายเสร็จสมบูรณ์เพื่อให้สามารถเข้าสู่ขั้นตอนการล้างข้อมูลได้ นี่ไม่ใช่ปัญหาสำหรับตัวรวบรวมแบบขนานที่หยุดโลก เนื่องจากโลกถูกหยุดชั่วคราวแล้ว (หมายเหตุของผู้แปล: เธรดแอปพลิเคชันถูกหยุดชั่วคราว และเธรดการรวบรวมขยะผูกขาดทรัพยากร) แต่สำหรับผู้สะสมพร้อมกัน อาจไม่ปลอดภัยที่จะเปลี่ยนจากขั้นตอนการทำเครื่องหมายไปเป็นขั้นตอนการทำความสะอาดทันที หากเธรดแอปพลิเคชันแก้ไขชิ้นส่วนของหน่วยความจำที่ได้รับการติดตามและทำเครื่องหมายโดยตัวรวบรวมขยะ การอ้างอิงที่ไม่ได้ทำเครื่องหมายใหม่อาจถูกสร้างขึ้น ในการใช้งานคอลเลกชันที่เกิดขึ้นพร้อมกันบางอย่าง สิ่งนี้อาจทำให้แอปพลิเคชันติดอยู่ในลูปของคำอธิบายประกอบซ้ำ ๆ เป็นเวลานานโดยไม่สามารถรับหน่วยความจำว่างได้เมื่อแอปพลิเคชันต้องการหน่วยความจำนี้
จากการสนทนาจนถึงตอนนี้ เราทราบว่ามีตัวรวบรวมขยะและอัลกอริธึมการรวบรวมขยะจำนวนมาก ซึ่งแต่ละตัวเหมาะสำหรับประเภทแอปพลิเคชันเฉพาะและโหลดที่แตกต่างกัน ไม่ใช่แค่อัลกอริธึมที่แตกต่างกัน แต่การใช้งานอัลกอริธึมที่แตกต่างกัน ดังนั้นจึงเป็นการดีที่สุดที่จะเข้าใจความต้องการของแอปพลิเคชันและคุณลักษณะของตนเองก่อนที่จะระบุตัวรวบรวมขยะ ต่อไป เราจะแนะนำข้อผิดพลาดบางประการของโมเดลหน่วยความจำแพลตฟอร์ม Java ข้อผิดพลาดในที่นี้อ้างอิงถึงสมมติฐานบางประการที่โปรแกรมเมอร์ Java มีแนวโน้มที่จะทำในสภาพแวดล้อมการผลิตที่เปลี่ยนแปลงแบบไดนามิก ซึ่งทำให้ประสิทธิภาพของแอปพลิเคชันแย่ลง
เหตุใดการปรับแต่งจึงไม่สามารถแทนที่การรวบรวมขยะได้
โปรแกรมเมอร์ Java ส่วนใหญ่รู้ว่ามีตัวเลือกมากมายในการเพิ่มประสิทธิภาพโปรแกรม Java JVM ตัวเลือกตัวรวบรวมขยะ และพารามิเตอร์การปรับแต่งประสิทธิภาพช่วยให้นักพัฒนาใช้เวลามากมายในการปรับแต่งประสิทธิภาพอย่างไม่มีที่สิ้นสุด เรื่องนี้ทำให้บางคนสรุปได้ว่าการรวบรวมขยะนั้นไม่ดี และการปรับแต่งเพื่อให้การรวบรวมขยะเกิดขึ้นไม่บ่อยหรือสั้นลงเป็นวิธีแก้ปัญหาที่ดี แต่ก็มีความเสี่ยง
พิจารณาการปรับแต่งแอปพลิเคชันเฉพาะ พารามิเตอร์การปรับแต่งส่วนใหญ่ (เช่น อัตราการจัดสรรหน่วยความจำ ขนาดออบเจ็กต์ เวลาตอบสนอง) ขึ้นอยู่กับอัตราการจัดสรรหน่วยความจำของแอปพลิเคชัน (หมายเหตุของผู้แปล: หรือพารามิเตอร์อื่นๆ) ตามปริมาณข้อมูลการทดสอบปัจจุบัน ในที่สุดมันก็อาจนำไปสู่ผลลัพธ์สองประการต่อไปนี้:
1. กรณีการใช้งานที่ผ่านการทดสอบจะล้มเหลวในการผลิต
2. การเปลี่ยนแปลงปริมาณข้อมูลหรือการเปลี่ยนแปลงในแอปพลิเคชันจำเป็นต้องมีการปรับแต่งใหม่
การปรับแต่งเป็นแบบวนซ้ำ และโดยเฉพาะตัวรวบรวมขยะที่ทำงานพร้อมกันอาจจำเป็นต้องมีการปรับแต่งจำนวนมาก (โดยเฉพาะอย่างยิ่งในสภาพแวดล้อมการใช้งานจริง) จำเป็นต้องใช้การวิเคราะห์พฤติกรรมเพื่อตอบสนองความต้องการของแอปพลิเคชัน เพื่อให้เป็นไปตามสถานการณ์กรณีที่เลวร้ายที่สุด ผลลัพธ์ของการปรับแต่งอาจเป็นการกำหนดค่าที่เข้มงวดมาก ซึ่งทำให้สิ้นเปลืองทรัพยากรจำนวนมาก วิธีการปรับแต่งนี้เป็นภารกิจที่แปลกประหลาด ในความเป็นจริง ยิ่งคุณปรับตัวรวบรวมขยะให้ตรงกับโหลดเฉพาะมากเท่าไร คุณก็จะยิ่งห่างไกลจากลักษณะไดนามิกของรันไทม์ Java มากขึ้นเท่านั้น ท้ายที่สุดแล้ว มีแอปพลิเคชันจำนวนเท่าใดที่มีโหลดที่เสถียร และคุณสามารถคาดหวังได้ว่าโหลดจะเชื่อถือได้เพียงใด
ดังนั้น หากคุณไม่ได้มุ่งเน้นไปที่การปรับแต่ง คุณสามารถทำอะไรได้บ้างเพื่อป้องกันข้อผิดพลาดหน่วยความจำไม่เพียงพอและปรับปรุงเวลาตอบสนอง สิ่งแรกคือการหาปัจจัยหลักที่ส่งผลต่อประสิทธิภาพของแอปพลิเคชัน Java
การกระจายตัว
ปัจจัยที่ส่งผลต่อประสิทธิภาพของแอปพลิเคชัน Java ไม่ใช่ตัวรวบรวมขยะ แต่เป็นการกระจายตัวและวิธีที่ตัวรวบรวมขยะจัดการการกระจายตัว การกระจายตัวที่เรียกว่าเป็นสถานะที่มีพื้นที่ว่างในพื้นที่ฮีป แต่ไม่มีพื้นที่หน่วยความจำที่ต่อเนื่องกันขนาดใหญ่พอที่จะจัดสรรหน่วยความจำสำหรับอ็อบเจ็กต์ใหม่ ตามที่กล่าวไว้ในบทความแรก การกระจายตัวของหน่วยความจำอาจเป็น TLAB ของพื้นที่ที่เหลืออยู่ในฮีป หรือพื้นที่ที่ครอบครองโดยวัตถุขนาดเล็กที่ถูกปล่อยออกมาท่ามกลางวัตถุที่มีอายุยาวนาน
เมื่อเวลาผ่านไปและในขณะที่แอปพลิเคชันทำงาน การกระจายตัวนี้จะกระจายไปทั่วฮีป ในบางกรณี การใช้พารามิเตอร์ที่ปรับแบบคงที่อาจทำให้แย่ลงได้ เนื่องจากไม่สามารถตอบสนองความต้องการแบบไดนามิกของแอปพลิเคชันได้ แอปพลิเคชันไม่สามารถใช้พื้นที่กระจัดกระจายนี้ได้อย่างมีประสิทธิภาพ การไม่ทำอะไรเลยจะส่งผลให้เกิดการรวบรวมขยะต่อเนื่องโดยที่ตัวรวบรวมขยะพยายามเพิ่มหน่วยความจำสำหรับการจัดสรรให้กับอ็อบเจ็กต์ใหม่ ในกรณีที่เลวร้ายที่สุด แม้แต่การรวบรวมขยะที่ต่อเนื่องกันก็ไม่สามารถเพิ่มหน่วยความจำได้มากขึ้น (การกระจายตัวมากเกินไป) จากนั้น JVM จะต้องส่งข้อผิดพลาดหน่วยความจำล้น คุณสามารถแก้ไขการแตกแฟรกเมนต์ได้โดยการรีสตาร์ทแอปพลิเคชันเพื่อให้ฮีป Java มีพื้นที่หน่วยความจำที่อยู่ติดกันเพื่อจัดสรรอ็อบเจ็กต์ใหม่ การรีสตาร์ทโปรแกรมจะทำให้ระบบหยุดทำงาน และหลังจากนั้นไม่นาน ฮีป Java จะเต็มไปด้วยแฟรกเมนต์อีกครั้ง และบังคับให้รีสตาร์ทอีกครั้ง
ข้อผิดพลาดหน่วยความจำไม่เพียงพอที่ทำให้กระบวนการหยุดทำงานและบันทึกที่แสดงว่าตัวรวบรวมขยะโอเวอร์โหลด บ่งชี้ว่าตัวรวบรวมขยะกำลังพยายามเพิ่มหน่วยความจำ และฮีปมีการแยกส่วนอย่างมาก โปรแกรมเมอร์บางคนจะพยายามแก้ไขปัญหาการกระจายตัวโดยการปรับตัวรวบรวมขยะให้เหมาะสมอีกครั้ง แต่ฉันคิดว่าเราควรหาวิธีใหม่ๆ มากกว่านี้ในการแก้ปัญหานี้ หัวข้อต่อไปนี้จะเน้นไปที่วิธีแก้ปัญหาสองประการสำหรับการแยกส่วน: การรวบรวมขยะตามรุ่นและการบดอัด
การเก็บขยะตามรุ่น
คุณอาจเคยได้ยินทฤษฎีที่ว่าออบเจ็กต์ส่วนใหญ่ในสภาพแวดล้อมการใช้งานจริงมีอายุสั้น การรวบรวมขยะตามรุ่นเป็นกลยุทธ์การรวบรวมขยะที่ได้มาจากทฤษฎีนี้ ในการรวบรวมขยะรุ่น เราจะแบ่งฮีปออกเป็นช่องว่าง (หรือรุ่น) ที่แตกต่างกัน และแต่ละพื้นที่จะจัดเก็บวัตถุที่มีอายุต่างกัน อายุของวัตถุคือจำนวนรอบการรวบรวมขยะที่วัตถุนั้นมีอยู่ (นั่นคือ วัตถุนั้นมีอายุเท่าใด) ยังคงอ้างอิงหลังจากรอบการรวบรวมขยะ)
เมื่อไม่มีพื้นที่เหลือให้คนรุ่นใหม่จัดสรร วัตถุที่ใช้งานอยู่ในรุ่นใหม่จะถูกย้ายไปยังรุ่นเก่า (โดยปกติจะมีเพียงสองรุ่นเท่านั้น หมายเหตุผู้แปล: เฉพาะวัตถุที่มีอายุตามที่กำหนดเท่านั้นที่จะย้ายไปยัง รุ่นเก่า) การรวบรวมขยะรุ่นมักจะใช้ตัวรวบรวมสำเนาทางเดียว หากคุณใช้นักสะสมแบบขนานหรือนักสะสมแบบลอกเลียนแบบ นักสะสมรุ่นเยาว์ของคุณก็คือนักสะสมที่หยุดโลก (ดูคำอธิบายก่อนหน้า)
รุ่นเก่าถูกจัดสรรให้กับวัตถุที่ถูกย้ายออกจากรุ่นใหม่ วัตถุเหล่านี้มีการอ้างอิงมาเป็นเวลานานหรือถูกอ้างอิงโดยคอลเลกชันของวัตถุในยุคใหม่ ในบางครั้ง วัตถุขนาดใหญ่จะถูกจัดสรรให้กับรุ่นเก่าโดยตรง เนื่องจากต้นทุนในการเคลื่อนย้ายวัตถุขนาดใหญ่ค่อนข้างสูง
เทคโนโลยีการเก็บขยะรุ่นต่อรุ่น
ในการรวบรวมขยะรุ่นต่างๆ การรวบรวมขยะจะทำงานน้อยลงในรุ่นเก่าและบ่อยมากขึ้นในรุ่นใหม่ และเรายังหวังว่าวงจรการรวบรวมขยะในรุ่นใหม่จะสั้นลงด้วย ในบางกรณีที่พบไม่บ่อยนัก คนรุ่นใหม่อาจถูกรวบรวมบ่อยกว่าคนรุ่นเก่า สิ่งนี้สามารถเกิดขึ้นได้หากคุณทำให้คนรุ่นใหม่มีขนาดใหญ่เกินไป และออบเจ็กต์ส่วนใหญ่ในแอปพลิเคชันของคุณใช้งานได้นาน ในกรณีนี้ หากรุ่นเก่าถูกตั้งค่าให้เล็กเกินไปที่จะรองรับวัตถุที่มีอายุยืนยาวทั้งหมด การเก็บขยะของรุ่นเก่าก็จะดิ้นรนเพื่อเพิ่มพื้นที่ว่างสำหรับวัตถุที่ถูกย้ายเข้าไป อย่างไรก็ตาม โดยทั่วไปแล้ว การรวบรวมขยะตามรุ่นสามารถช่วยให้แอปพลิเคชันได้รับประสิทธิภาพที่ดีขึ้น
ข้อดีอีกประการของการแบ่งคนรุ่นใหม่คือสามารถแก้ไขปัญหาการแตกแฟรกเมนต์ได้ในระดับหนึ่งหรือเลื่อนสถานการณ์กรณีที่เลวร้ายที่สุดออกไป วัตถุขนาดเล็กเหล่านั้นที่มีเวลารอดชีวิตสั้นอาจทำให้เกิดปัญหาการกระจัดกระจาย แต่พวกมันทั้งหมดจะถูกทำความสะอาดในการรวบรวมขยะรุ่นใหม่ เนื่องจากวัตถุที่มีอายุยืนยาวจะได้รับการจัดสรรพื้นที่ขนาดกะทัดรัดมากขึ้นเมื่อย้ายไปยังรุ่นเก่า รุ่นเก่าจึงมีขนาดกะทัดรัดมากขึ้นเช่นกัน เมื่อเวลาผ่านไป (หากแอปพลิเคชันของคุณทำงานนานพอ) รุ่นเก่าก็จะมีการแยกส่วน ทำให้ต้องมีการรวบรวมขยะทั้งหมดอย่างน้อยหนึ่งรายการจึงจะรันได้ และ JVM ก็อาจทำให้เกิดข้อผิดพลาดหน่วยความจำไม่เพียงพอได้เช่นกัน แต่การแยกคนรุ่นใหม่ออกไปจะทำให้สถานการณ์ที่เลวร้ายที่สุดเลื่อนออกไป ซึ่งเพียงพอสำหรับการใช้งานหลายๆ อย่าง สำหรับแอปพลิเคชันส่วนใหญ่ จะลดความถี่ของการรวบรวมขยะแบบหยุดโลกและโอกาสที่จะเกิดข้อผิดพลาดหน่วยความจำไม่เพียงพอ
เพิ่มประสิทธิภาพการรวบรวมขยะตามรุ่น
ดังที่กล่าวไปแล้ว การใช้ Generational Garbage นำมาซึ่งการปรับแต่งซ้ำๆ เช่น ปรับขนาดคนรุ่นใหม่ อัตราการเลื่อนตำแหน่ง เป็นต้น ฉันไม่สามารถเน้นย้ำถึงข้อดีข้อเสียของรันไทม์แอปพลิเคชันเฉพาะได้: การเลือกขนาดคงที่จะปรับแอปพลิเคชันให้เหมาะสม แต่ยังลดความสามารถของตัวรวบรวมขยะในการรับมือกับการเปลี่ยนแปลงแบบไดนามิกซึ่งหลีกเลี่ยงไม่ได้
หลักการแรกสำหรับคนรุ่นใหม่คือเพิ่มให้มากที่สุดเท่าที่จะเป็นไปได้โดยรับประกันความล่าช้าในการรวบรวมขยะแบบหยุดโลก และในขณะเดียวกันก็สงวนพื้นที่ในฮีปให้เพียงพอสำหรับวัตถุที่รอดตายในระยะยาว ต่อไปนี้เป็นปัจจัยเพิ่มเติมที่ควรพิจารณาเมื่อปรับแต่งตัวรวบรวมขยะรุ่น:
1. คนรุ่นใหม่ส่วนใหญ่เป็นคนเก็บขยะหยุดโลก ดังนั้น สำหรับแอปพลิเคชันที่ได้รับผลกระทบอย่างมากจากเวลาหยุดการเก็บขยะชั่วคราว ให้พิจารณาอย่างรอบคอบว่าคนรุ่นใหม่มีขนาดใหญ่เพียงใด
2. อัลกอริธึมการรวบรวมขยะที่แตกต่างกันสามารถนำไปใช้กับรุ่นที่แตกต่างกันได้ เช่น การเก็บขยะแบบคู่ขนานถูกใช้กับคนรุ่นใหม่ และการเก็บขยะพร้อมกันกับคนรุ่นเก่า
3. เมื่อพบว่ามีการเลื่อนตำแหน่งบ่อยครั้ง (หมายเหตุนักแปล: การย้ายจากคนรุ่นใหม่ไปสู่รุ่นเก่า) ล้มเหลว แสดงว่าคนรุ่นเก่ามีชิ้นส่วนมากเกินไป หมายความว่า คนรุ่นเก่ามีพื้นที่ไม่เพียงพอ เพื่อจัดเก็บสิ่งของที่ย้ายมาจากคนรุ่นใหม่ ณ จุดนี้ คุณสามารถปรับอัตราโปรโมชันได้ (เช่น ปรับอายุโปรโมชัน) หรือตรวจสอบให้แน่ใจว่าอัลกอริธึมการรวบรวมขยะในรุ่นเก่ากำลังทำการบีบอัด (จะกล่าวถึงในย่อหน้าถัดไป) และปรับการบีบอัดให้เหมาะกับโหลดของแอปพลิเคชัน . นอกจากนี้ยังสามารถเพิ่มขนาดฮีปและขนาดแต่ละรุ่นได้ แต่จะขยายเวลาหยุดชั่วคราวในรุ่นเก่าเพิ่มเติม รู้ว่าการแยกส่วนเป็นสิ่งที่หลีกเลี่ยงไม่ได้
4. การเก็บขยะแบบ Generational เหมาะสมที่สุดสำหรับการใช้งานดังกล่าว ขยะเหล่านี้จะมีวัตถุขนาดเล็กจำนวนมากและมีอายุการใช้งานสั้นมาก สำหรับแอปพลิเคชันดังกล่าว การรวบรวมขยะทั่วไปสามารถลดการกระจายตัวของข้อมูลได้อย่างมีประสิทธิภาพ และชะลอผลกระทบของการกระจายตัวของข้อมูล
การบีบอัด
แม้ว่าการรวบรวมขยะรุ่นทั่วไปจะชะลอการเกิดการกระจายตัวของข้อมูลและข้อผิดพลาดหน่วยความจำไม่เพียงพอ การบีบอัดเป็นวิธีแก้ปัญหาที่แท้จริงเพียงทางเดียวสำหรับปัญหาการกระจายตัวของข้อมูล การบดอัดเป็นกลยุทธ์การรวบรวมขยะที่ปล่อยบล็อกหน่วยความจำที่ต่อเนื่องกันโดยการเคลื่อนย้ายวัตถุไปรอบๆ ซึ่งจะทำให้มีพื้นที่ว่างเพียงพอที่จะสร้างวัตถุใหม่
การย้ายอ็อบเจ็กต์และการอัปเดตการอ้างอิงอ็อบเจ็กต์เป็นการดำเนินการหยุดโลกที่จะนำมาซึ่งปริมาณการใช้ที่แน่นอน (โดยมีข้อยกเว้นประการหนึ่งซึ่งจะกล่าวถึงในบทความถัดไปในชุดนี้) ยิ่งวัตถุมีชีวิตรอดได้มากเท่าไร เวลาหยุดชั่วคราวที่เกิดจากการบดอัดก็จะนานขึ้นเท่านั้น ในสถานการณ์ที่มีพื้นที่เหลือน้อยและการกระจายตัวอย่างรุนแรง (โดยปกติเนื่องจากโปรแกรมทำงานมาเป็นเวลานาน) อาจมีการหยุดชั่วครู่ชั่วขณะในพื้นที่บีบอัดที่มีออบเจ็กต์สดจำนวนมาก และเมื่อเข้าใกล้หน่วยความจำล้น การบีบอัดข้อมูล ฮีปทั้งหมดอาจใช้เวลาหลายสิบวินาทีด้วยซ้ำ
เวลาหยุดชั่วคราวสำหรับการกระชับจะขึ้นอยู่กับจำนวนหน่วยความจำที่ต้องย้ายและจำนวนข้อมูลอ้างอิงที่ต้องอัปเดต การวิเคราะห์ทางสถิติแสดงให้เห็นว่ายิ่งฮีปมีขนาดใหญ่เท่าใด จำนวนอ็อบเจ็กต์สดที่ต้องย้ายและอัปเดตการอ้างอิงก็จะยิ่งมากขึ้นเท่านั้น เวลาหยุดชั่วคราวคือประมาณ 1 วินาทีสำหรับทุกๆ 1GB ถึง 2GB ของวัตถุสดที่ถูกย้าย และสำหรับฮีปขนาด 4GB มีแนวโน้มว่าจะมีวัตถุสด 25% ดังนั้นจะมีการหยุดชั่วคราวเป็นครั้งคราวประมาณ 1 วินาที
ผนังหน่วยความจำการบีบอัดและแอปพลิเคชัน
ผนังหน่วยความจำของแอปพลิเคชันหมายถึงขนาดฮีปที่สามารถตั้งค่าได้ก่อนการหยุดชั่วคราวที่เกิดจากการรวบรวมขยะ (เช่น การบดอัด) ขึ้นอยู่กับระบบและแอพพลิเคชั่น ผนังหน่วยความจำแอพพลิเคชั่น Java ส่วนใหญ่มีตั้งแต่ 4GB ถึง 20GB นี่คือเหตุผลว่าทำไมแอปพลิเคชันระดับองค์กรส่วนใหญ่จึงถูกปรับใช้บน JVM ขนาดเล็กหลายตัว แทนที่จะเป็น JVM ขนาดใหญ่สองสามตัว ลองพิจารณาสิ่งนี้: มีการออกแบบและการปรับใช้แอปพลิเคชัน Java ระดับองค์กรสมัยใหม่จำนวนเท่าใดที่กำหนดโดยข้อจำกัดการบีบอัดของ JVM ในกรณีนี้ เพื่อหลีกเลี่ยงเวลาหยุดชั่วคราวของการจัดเรียงข้อมูลฮีป เราจึงตัดสินใจปรับใช้หลายอินสแตนซ์ซึ่งมีค่าใช้จ่ายในการจัดการสูงกว่า นี่ค่อนข้างแปลกเมื่อพิจารณาถึงความสามารถในการจัดเก็บข้อมูลขนาดใหญ่ของฮาร์ดแวร์ในปัจจุบันและความต้องการหน่วยความจำที่เพิ่มขึ้นสำหรับแอปพลิเคชัน Java ระดับองค์กร เหตุใดจึงตั้งค่าหน่วยความจำเพียงไม่กี่ GB สำหรับแต่ละอินสแตนซ์ การบีบอัดพร้อมกันจะทำลายกำแพงหน่วยความจำซึ่งเป็นหัวข้อของบทความถัดไปของฉัน
สรุป
บทความนี้เป็นบทความเบื้องต้นเกี่ยวกับการรวบรวมขยะเพื่อช่วยให้คุณเข้าใจแนวคิดและกลไกของการรวบรวมขยะ และหวังว่าจะกระตุ้นให้คุณอ่านบทความที่เกี่ยวข้องเพิ่มเติม มีหลายสิ่งที่กล่าวถึงในที่นี้มานานแล้ว และจะมีการแนะนำแนวคิดใหม่ๆ บางอย่างในบทความถัดไป ตัวอย่างเช่น Zing JVM ของ Azul มีการใช้การบีบอัดพร้อมกัน เป็นเทคโนโลยีการรวบรวมขยะที่เกิดขึ้นใหม่ที่พยายามกำหนดรูปแบบหน่วยความจำ Java ใหม่ โดยเฉพาะอย่างยิ่งในขณะที่หน่วยความจำและพลังการประมวลผลยังคงปรับปรุงอย่างต่อเนื่องในปัจจุบัน
ต่อไปนี้เป็นประเด็นสำคัญบางประการเกี่ยวกับการรวบรวมขยะที่ฉันได้สรุปไว้:
1. อัลกอริธึมการรวบรวมขยะและการใช้งานที่แตกต่างกันจะปรับให้เข้ากับความต้องการของแอปพลิเคชันที่แตกต่างกัน ตัวรวบรวมขยะแบบติดตามคือตัวรวบรวมขยะที่ใช้กันมากที่สุดในเครื่องเสมือน Java เชิงพาณิชย์
2. การรวบรวมขยะแบบขนานจะใช้ทรัพยากรทั้งหมดพร้อมกันเมื่อดำเนินการรวบรวมขยะ โดยปกติแล้วจะเป็นตัวรวบรวมขยะแบบหยุดโลกและมีปริมาณงานที่สูงกว่า แต่เธรดผู้ปฏิบัติงานของแอปพลิเคชันต้องรอให้เธรดการรวบรวมขยะเสร็จสมบูรณ์ ซึ่งมีผลกระทบบางอย่างต่อเวลาตอบสนองของแอปพลิเคชัน
3. การรวบรวมขยะพร้อมกัน: ในขณะที่กำลังดำเนินการรวบรวม เธรดผู้ปฏิบัติงานแอปพลิเคชันยังคงทำงานอยู่ ตัวรวบรวมขยะที่เกิดขึ้นพร้อมกันจำเป็นต้องรวบรวมขยะให้เสร็จสิ้นก่อนที่แอปพลิเคชันจะต้องการหน่วยความจำ
4. การรวบรวมขยะตามรุ่นจะช่วยชะลอการแตกแฟรกเมนต์ แต่ไม่สามารถกำจัดการแตกแฟรกเมนต์ได้ การรวบรวมขยะทั่วไปจะแบ่งฮีปออกเป็นสองช่องว่าง ช่องว่างหนึ่งสำหรับออบเจ็กต์ใหม่ และอีกช่องหนึ่งสำหรับออบเจ็กต์เก่า การรวบรวมขยะทั่วไปเหมาะสำหรับการใช้งานกับวัตถุขนาดเล็กจำนวนมากซึ่งมีอายุการใช้งานสั้น
5. การบีบอัดเป็นวิธีเดียวที่จะแก้ปัญหาการกระจายตัว ตัวรวบรวมขยะส่วนใหญ่ทำการบีบอัดในลักษณะหยุดโลก ยิ่งโปรแกรมรันนานเท่าใด การอ้างอิงอ็อบเจ็กต์ก็จะซับซ้อนมากขึ้น และขนาดอ็อบเจ็กต์ก็จะกระจายไม่สม่ำเสมอมากขึ้น ซึ่งจะนำไปสู่เวลาในการบีบอัดนานขึ้น ขนาดของฮีปยังส่งผลต่อเวลาในการบีบอัดด้วย เนื่องจากอาจมีออบเจ็กต์สดและการอ้างอิงเพิ่มเติมที่จำเป็นต้องได้รับการอัปเดต
6. การปรับแต่งช่วยชะลอข้อผิดพลาดหน่วยความจำล้น แต่ผลลัพธ์ของการปรับแต่งมากเกินไปคือการกำหนดค่าที่เข้มงวด ก่อนที่คุณจะเริ่มปรับแต่งด้วยวิธีลองผิดลองถูก ตรวจสอบให้แน่ใจว่าคุณเข้าใจโหลดในสภาพแวดล้อมการผลิตของคุณ ประเภทออบเจ็กต์ของแอปพลิเคชันของคุณ และลักษณะของการอ้างอิงออบเจ็กต์ของคุณ การกำหนดค่าที่เข้มงวดเกินไปอาจไม่สามารถรองรับโหลดไดนามิกได้ ดังนั้นโปรดทำความเข้าใจผลที่ตามมาเมื่อตั้งค่าที่ไม่ใช่ไดนามิก
บทความถัดไปในชุดนี้คือ: การอภิปรายเชิงลึกเกี่ยวกับอัลกอริธึมการรวบรวมขยะ C4 (Concurrent Continuously Compacting Collector) ดังนั้นโปรดคอยติดตาม!
(จบข้อความฉบับเต็ม)