Java ที่มีประสิทธิภาพ พูดว่า:
มีการลงโทษด้านประสิทธิภาพขั้นรุนแรงสำหรับการใช้โปรแกรมสรุปผล
เหตุใดจึงช้ากว่าในการทำลายวัตถุโดยใช้เครื่องสรุปผล?
Java ที่มีประสิทธิภาพ พูดว่า:
มีการลงโทษด้านประสิทธิภาพขั้นรุนแรงสำหรับการใช้โปรแกรมสรุปผล
เหตุใดจึงช้ากว่าในการทำลายวัตถุโดยใช้เครื่องสรุปผล?
เพราะวิธีการทำงานของคนเก็บขยะ เพื่อประสิทธิภาพ Java GC ส่วนใหญ่ใช้ตัวรวบรวมการคัดลอก โดยที่อ็อบเจ็กต์อายุสั้นจะถูกจัดสรรลงในบล็อกหน่วยความจำ "eden" และเมื่อถึงเวลาที่อ็อบเจ็กต์รุ่นนั้นจะถูกรวบรวม GC เพียงแค่ต้องคัดลอกอ็อบเจ็กต์ที่ ยังคง "มีชีวิต" อยู่ในพื้นที่เก็บข้อมูลถาวร จากนั้นจึงสามารถล้าง (ฟรี) บล็อกหน่วยความจำ "eden" ทั้งหมดได้ในคราวเดียว สิ่งนี้มีประสิทธิภาพเนื่องจากโค้ด Java ส่วนใหญ่จะสร้างอินสแตนซ์ของอ็อบเจ็กต์หลายพันอินสแตนซ์ (แบบพื้นฐานชนิดบรรจุกล่อง อาร์เรย์ชั่วคราว ฯลฯ) โดยมีอายุการใช้งานเพียงไม่กี่วินาที
เมื่อคุณมีผู้เข้ารอบสุดท้ายผสมอยู่ GC จะไม่สามารถล้างรุ่นทั้งหมดพร้อมกันได้ แต่จำเป็นต้องค้นหาออบเจ็กต์ทั้งหมดในรุ่นนั้นที่จำเป็นต้องทำการสรุป และจัดคิวไว้บนเธรดที่ดำเนินการกับโปรแกรมสรุปผลจริง ๆ ในระหว่างนี้ GC ไม่สามารถทำความสะอาดวัตถุได้อย่างมีประสิทธิภาพ ดังนั้นจึงต้องรักษาพวกมันให้มีชีวิตอยู่นานกว่าที่ควรจะเป็น หรือต้องชะลอการรวบรวมวัตถุอื่น ๆ หรือทั้งสองอย่าง นอกจากนี้คุณยังมีเวลารอตามอำเภอใจในการดำเนินการรอบสุดท้ายจริงๆ
ปัจจัยทั้งหมดเหล่านี้รวมกันทำให้เกิดการลงโทษรันไทม์ที่สำคัญ ซึ่งเป็นเหตุผลว่าทำไมการสรุปแบบกำหนด (โดยใช้เมธอด close()
หรือที่คล้ายกันเพื่อสรุปสถานะของออบเจ็กต์อย่างชัดเจน) จึงเป็นที่ต้องการ
FileOutputStream
ดังนั้นจึงไม่น่าจะเป็นไปได้ที่ตัวสรุปสำหรับบางอ็อบเจ็กต์จะทำให้ GC ของอ็อบเจ็กต์ที่ไม่ได้ใช้ตัวสรุปนั้นล่าช้า เนื่องจากโปรแกรมส่วนใหญ่จะได้รับผลกระทบ
- person Raedwald; 01.08.2014
FileOutputStream
มีตัวสรุป ซึ่งคุณสามารถดูได้โดยดูที่แหล่งที่มาของ OpenJDK (แต่ฉันไม่พบสิ่งใดที่ต้องการการใช้งานไลบรารีมาตรฐานเพื่อใช้ตัวสรุป) ดังนั้นในทางปฏิบัติ ออบเจ็กต์ที่มีสิทธิ์สำหรับ GC แต่ยังคงรอการสรุปจะถูกเลื่อนระดับเป็น รุ่นเก่าต่อไป (พื้นที่ผู้รอดชีวิตหรือดำรงตำแหน่ง) ในขณะที่ผู้เข้ารอบสุดท้ายอยู่ในคิวให้ทำงาน แต่ความทรงจำที่แท้จริงจะไม่ถูกเรียกคืนจนกว่าจะรวบรวมคนรุ่นเก่าต่อไป
- person Daniel Pryden; 01.08.2014
close()
และ finalize()
โอเวอร์เฮดนี้จะเกิดขึ้นด้วยหรือไม่หากเราเรียก close()
อย่างชัดเจน
- person Gerardo Cauich; 26.06.2021
เมื่อประสบปัญหาดังกล่าวจริง ๆ แล้ว:
ใน Sun HotSpot JVM ตัวสรุปจะถูกประมวลผลบนเธรดที่ได้รับลำดับความสำคัญคงที่และต่ำ ในแอปพลิเคชันที่มีโหลดสูง เป็นเรื่องง่ายที่จะสร้างออบเจ็กต์ที่ต้องการการสรุปผลได้เร็วกว่าที่เธรดการสรุปผลที่มีลำดับความสำคัญต่ำสามารถประมวลผลได้ ในขณะเดียวกัน พื้นที่บนฮีปที่ใช้โดยออบเจ็กต์ที่รอการสรุปจะไม่พร้อมใช้งานสำหรับการใช้งานอื่นๆ ในที่สุด แอปพลิเคชันของคุณอาจใช้เวลาทั้งหมดไปกับการรวบรวมขยะ เนื่องจากหน่วยความจำที่มีอยู่ทั้งหมดถูกใช้โดยอ็อบเจ็กต์ที่รอการสรุป
แน่นอนว่านี่คือ นอกเหนือจากเหตุผลอื่นๆ มากมายที่จะไม่ใช้ตัวสรุปที่อธิบายไว้ใน Effective Java
ฉันเพิ่งหยิบสำเนา Effective Java ขึ้นมาจากโต๊ะเพื่อดูว่าเขาหมายถึงอะไร
หากคุณอ่านบทที่ 2 ตอนที่ 6 เขาจะอธิบายรายละเอียดที่ดีเกี่ยวกับการแสดงยอดนิยมต่างๆ
You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.
ฉันอยากจะแนะนำให้อ่านเนื้อหาทั้งหมด - มันอธิบายสิ่งต่าง ๆ ได้ดีกว่าที่ฉันจะสามารถนกแก้วได้ที่นี่
หากคุณอ่านเอกสารประกอบของ Finalize() อย่างใกล้ชิด คุณจะสังเกตเห็นว่า Finalizer เปิดใช้งานออบเจ็กต์เพื่อป้องกันไม่ให้ GC รวบรวม
หากไม่มีตัวปิดท้าย วัตถุก็สามารถลบออกได้และไม่ต้องการการดูแลอีกต่อไป แต่หากมีตัวสรุป จะต้องตรวจสอบในภายหลัง หากวัตถุนั้นไม่ "มองเห็น" ได้อีก
โดยไม่ทราบแน่ชัดว่าการรวบรวมขยะ Java ปัจจุบันถูกนำไปใช้อย่างไร (อันที่จริงแล้ว เนื่องจากมีการใช้งาน Java ที่แตกต่างกันออกไป จึงมี GC ที่แตกต่างกันด้วย) คุณสามารถสรุปได้ว่า GC จะต้องทำงานเพิ่มเติมบางอย่างหากวัตถุมีตัวสรุป เนื่องจาก ของคุณสมบัตินี้
ความคิดของฉันคือ: Java เป็นภาษาที่รวบรวมขยะซึ่งจัดสรรหน่วยความจำตามอัลกอริธึมภายในของตัวเอง บ่อยครั้งที่ GC จะสแกนฮีป กำหนดว่าออบเจ็กต์ใดจะไม่ถูกอ้างอิงอีกต่อไป และยกเลิกการจัดสรรหน่วยความจำ Finalizer ขัดจังหวะสิ่งนี้และบังคับให้มีการจัดสรรหน่วยความจำนอกวงจร GC ซึ่งอาจทำให้เกิดความไร้ประสิทธิภาพ ฉันคิดว่าแนวทางปฏิบัติที่ดีที่สุดคือการใช้ตัวสรุปเมื่อจำเป็นเท่านั้น เช่น การเพิ่มตัวจัดการไฟล์หรือการปิดการเชื่อมต่อฐานข้อมูลซึ่งควรทำตามที่กำหนดไว้
เหตุผลหนึ่งที่ฉันคิดได้ก็คือการล้างหน่วยความจำอย่างชัดเจนนั้นไม่จำเป็นหากทรัพยากรของคุณคือ Java Objects ทั้งหมด ไม่ใช่โค้ดเนทีฟ