System.gc () ทำอะไรเมื่อไหร่


118

ฉันรู้ว่าการรวบรวมขยะเป็นแบบอัตโนมัติใน Java แต่ฉันเข้าใจว่าถ้าคุณเรียกSystem.gc()รหัสของคุณว่า JVM อาจตัดสินใจหรือไม่ดำเนินการเก็บขยะ ณ จุดนั้น วิธีนี้ทำงานได้อย่างแม่นยำ? สิ่งที่ตาม / พารามิเตอร์ที่ไม่ตรง JVM ที่ตัดสินใจที่จะทำ (หรือไม่ทำ) GC เมื่อมันเห็นSystem.gc()?

มีตัวอย่างใดบ้างที่ควรใส่ในโค้ดของคุณในกรณีนี้


4
มีแนวคิดที่ซับซ้อนเกินไปสำหรับรายละเอียดทั้งหมดที่นี่ แต่บทความนี้จะช่วยคุณได้: busyjava.com/posts/how-does-garbage-collection-work
GEOCHET

พฤติกรรมที่แน่นอนขึ้นอยู่กับ JVM กล่าวคือขึ้นอยู่กับการนำไปใช้งาน
Thorbjørn Ravn Andersen

คำตอบ:


59

ในทางปฏิบัติมักจะตัดสินใจทำการเก็บขยะ คำตอบจะแตกต่างกันไปขึ้นอยู่กับปัจจัยหลายประการเช่น JVM ที่คุณกำลังใช้งานอยู่ในโหมดใดและอัลกอริทึมการรวบรวมขยะใดที่ใช้อยู่

ฉันจะไม่ใช้มันในรหัสของคุณ หาก JVM กำลังจะโยน OutOfMemoryError การเรียก System.gc () จะไม่หยุดเพราะตัวเก็บขยะจะพยายามปลดปล่อยให้มากที่สุดก่อนที่จะไปถึงจุดสูงสุดนั้น ครั้งเดียวที่ฉันเคยเห็นมันใช้ในทางปฏิบัติคือใน IDE ที่มันติดอยู่กับปุ่มที่ผู้ใช้สามารถคลิกได้ แต่ถึงอย่างนั้นมันก็ไม่มีประโยชน์มากนัก


3
คุณสามารถบังคับเก็บขยะใน jconsole
Benedikt Waldvogel

25
@bene คุณ "บังคับ" ได้จริงหรือ? jconsole เรียก System.gc () หรือเปล่า
John Meagher

4
ฉันใช้System.gc()ขณะอยู่ในหน้าจอโหลดวิดีโอเกม ณ จุดนั้นฉันจะไม่สนใจจริงๆว่าการโทรเคลียร์ทุกอย่างที่เป็นไปได้หรือไม่ได้ทำอะไรเลย อย่างไรก็ตามฉันชอบที่จะ "ใช้ความพยายามในการรีไซเคิลวัตถุที่ไม่ใช้แล้ว" ในระหว่างหน้าจอโหลดมากกว่าในระหว่างการเล่นเกม
Kröw

30

ตัวอย่างเดียวที่ฉันคิดได้ว่าควรเรียก System.gc () ตรงไหนคือเมื่อสร้างโปรไฟล์แอปพลิเคชันเพื่อค้นหาการรั่วไหลของหน่วยความจำที่เป็นไปได้ ฉันเชื่อว่าผู้สร้างโปรไฟล์เรียกวิธีนี้ก่อนที่จะถ่ายภาพหน่วยความจำ


4
ใช่นั่นคือสิ่งที่ฉันทำเช่นกัน ค่าที่ส่งคืนโดย Runtime.freeMemory () เช่นมีความหมายจริงๆหลังจาก System.gc () เท่านั้นเพราะโดยปกติคุณต้องการทราบว่าอินสแตนซ์ที่ไม่สามารถรวบรวมหน่วยความจำถูกบล็อกได้เท่าใด แน่นอนว่าจะใช้ในการดีบัก / การทำโปรไฟล์หน่วยความจำเท่านั้นไม่เคยอยู่ในการผลิต
sleske

น่ารักดีสำหรับสถานการณ์อื่น ๆ ด้วย ตัวอย่างเช่นสมมติว่าคุณมีรูปแบบเดียวที่ทราบถึงวงจรชีวิต ใน onStop () หากคุณเป็นโมฆะ (ing) อินสแตนซ์คุณควรเรียก System.gc () เพื่อช่วย
portfoliobuilder

26

ข้อกำหนดภาษา Java ไม่รับประกันว่า JVM จะเริ่ม GC เมื่อคุณเรียกSystem.gc()ใช้ นี่คือเหตุผลที่ "อาจตัดสินใจทำ GC ณ จุดนั้นหรือไม่ก็ได้"

ตอนนี้ถ้าคุณดูซอร์สโค้ด OpenJDKซึ่งเป็นกระดูกสันหลังของ Oracle JVM คุณจะเห็นว่าการเรียกเพื่อSystem.gc()เริ่มวงจร GC หากคุณใช้ JVM อื่นเช่น J9 คุณต้องตรวจสอบเอกสารประกอบเพื่อหาคำตอบ ตัวอย่างเช่น JVM ของ Azul มีตัวเก็บขยะที่ทำงานอย่างต่อเนื่องดังนั้นการเรียกร้องให้System.gc()จะไม่ทำอะไรเลย

คำตอบอื่น ๆ กล่าวถึงการเริ่ม GC ใน JConsole หรือ VisualVM System.gc()โดยทั่วไปเครื่องมือเหล่านี้ทำให้การโทรทางไกลไปยัง

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

อย่างไรก็ตามมีบางกรณีที่System.gc()อาจเข้าใจการเรียกร้อง ตัวอย่างเช่นพิจารณา microbenchmarks ไม่มีใครอยากให้วงจร GC เกิดขึ้นกลางไมโครเบนช์มาร์ก ดังนั้นคุณอาจเรียกใช้วงจร GC ระหว่างการวัดแต่ละครั้งเพื่อให้แน่ใจว่าการวัดทุกครั้งเริ่มต้นด้วยฮีปว่าง


26

คุณไม่สามารถควบคุม GC ใน java ได้ - VM จะตัดสินใจ ผมไม่เคยทำงานในกรณีที่System.gc()เป็นความจำเป็น ตั้งแต่System.gc()การโทรเพียงแค่แสดงให้เห็นว่า VM ทำเก็บขยะและยังไม่ได้เก็บขยะเต็ม (คนรุ่นเก่าและใหม่ในกองหลาย generational) แล้วมันสามารถจริงสาเหตุเพิ่มเติมรอบการทำงานที่จะบริโภคเกินความจำเป็น

ในบางกรณีอาจแนะนำให้ VM ทำคอลเลคชันเต็มรูปแบบทันทีเนื่องจากคุณอาจทราบว่าแอปพลิเคชันจะไม่ได้ใช้งานในอีกไม่กี่นาทีข้างหน้าก่อนที่จะมีการยกของหนักขึ้น ตัวอย่างเช่นทันทีหลังจากการเริ่มต้นของวัตถุชั่วคราวจำนวนมากในระหว่างการเริ่มต้นแอปพลิเคชัน (กล่าวคือฉันเพิ่งแคชข้อมูลไว้เป็นตันและฉันรู้ว่าฉันจะไม่ได้รับกิจกรรมมากนักเป็นเวลาหนึ่งนาที) ลองนึกถึง IDE เช่น eclipse เริ่มต้น - มันทำอะไรได้มากมายในการเริ่มต้นดังนั้นบางทีหลังจากเริ่มต้นทันทีมันก็สมเหตุสมผลที่จะทำ gc แบบเต็ม ณ จุดนั้น


17

System.gc()คุณจะต้องระวังให้มากถ้าคุณเรียก การเรียกใช้สามารถเพิ่มปัญหาด้านประสิทธิภาพที่ไม่จำเป็นให้กับแอปพลิเคชันของคุณและไม่รับประกันว่าจะดำเนินการรวบรวมได้จริง มันเป็นไปได้จริงในการปิดการใช้งานอย่างชัดเจนผ่านอาร์กิวเมนต์จาวาSystem.gc()-XX:+DisableExplicitGC

ฉันขอแนะนำให้อ่านเอกสารที่มีอยู่ในJava HotSpot Garbage Collectionสำหรับรายละเอียดเชิงลึกเพิ่มเติมเกี่ยวกับการรวบรวมขยะ


แอปพลิเคชัน Android ของฉันมักจะส่ง OutOfMemoryException ให้ฉัน ดังนั้นฉันจึงคิดที่จะใช้ System.gc () ในฟังก์ชัน onDestroy ของคลาสของฉันเพื่อล้างการอ้างอิงและวัตถุที่ไม่ต้องการทั้งหมด เป็นแนวทางที่ดีหรือไม่
Sagar Devanga

2
@Sagar Devanga การโทรหา gc ด้วยตนเองตามที่คุณอธิบายไม่น่าจะช่วยได้ ก่อนที่จะโยน OOM JVM จะพยายามรวบรวมขยะเพื่อเพิ่มหน่วยความจำ
Scrubbie

10

System.gc()ถูกนำไปใช้โดย VM และสิ่งที่ทำคือการใช้งานเฉพาะ ผู้ดำเนินการสามารถส่งคืนและไม่ทำอะไรเลยตัวอย่างเช่น

สำหรับเวลาที่จะออกการรวบรวมด้วยตนเองครั้งเดียวที่คุณอาจต้องการทำเช่นนี้คือเมื่อคุณละทิ้งคอลเลคชันขนาดใหญ่ที่มีคอลเลกชันขนาดเล็กจำนวนมาก Map<String,<LinkedList>> ตัวอย่างเช่น - และคุณต้องการลองใช้การตีแบบเพอร์ฟแล้วและ ที่นั่น แต่ส่วนใหญ่คุณไม่ควรกังวลกับมัน GC รู้ดีกว่าคุณ - น่าเศร้า - เกือบตลอดเวลา


8

หากคุณใช้บัฟเฟอร์หน่วยความจำโดยตรง JVM จะไม่เรียกใช้ GC ให้คุณแม้ว่าคุณจะมีหน่วยความจำโดยตรงเหลือน้อยก็ตาม

ถ้าคุณโทรByteBuffer.allocateDirect()และคุณได้รับ OutOfMemoryError คุณสามารถหาสายนี้จะปรับหลังจากวิกฤติ GC ด้วยตนเอง


คุณกำลังบอกว่า System.gc () รวบรวมการจัดสรรโดยตรงในขณะที่ JVM โดยปกติจะไม่ (ก่อนที่จะโยน OOM) เพราะคิดว่าไม่ถูกต้อง. เหตุผลเดียวที่การจัดสรรอาจประสบความสำเร็จหลังจาก GC ที่ชัดเจนคือเวลาที่เพิ่มขึ้นและความเป็นไปได้มากขึ้นของวัตถุในฮีปที่มีตัวสุดท้ายที่เป็นอิสระ (และปลดปล่อยวัตถุที่จัดสรรโดยตรงบางส่วน)
eckes

ใน Java 6 เวอร์ชันล่าสุดโค้ดในจัดสรรบัฟเฟอร์จะเรียกใช้ System.gc () ก่อนที่จะโยน OutOfMemoryError ในรหัสการสรุปจะมีทางลัดCleanersที่หน่วยความจำโดยตรงใช้ กล่าวคือมันไม่ได้เป็นอิสระจากเธรดสุดท้ายเนื่องจากอาจช้าเกินไป ถึงกระนั้นก็เป็นไปได้ที่จะได้รับ OOME หากคุณสร้างพื้นที่หน่วยความจำโดยตรงอย่างรวดเร็วเป็นพิเศษ วิธีแก้ปัญหาคืออย่าทำเช่นนั้นและใช้พื้นที่หน่วยความจำโดยตรงของคุณซ้ำเมื่อคุณสามารถทำได้
Peter Lawrey

1
ขอบคุณดูเหมือนประสบการณ์ของฉันมากกว่า ดังนั้นฉันเดาว่าคำตอบนั้นถูกต้องสำหรับ Java เวอร์ชันเก่าเท่านั้น
eckes

5

JVM ส่วนใหญ่จะเริ่มต้น GC (ขึ้นอยู่กับสวิตช์ -XX: DiableExplicitGC และ -XX: + ExplicitGCInvokesConcurrent) แต่ข้อกำหนดนั้นมีการกำหนดไว้อย่างดีน้อยกว่าเพื่อให้สามารถใช้งานได้ดีขึ้นในภายหลัง

ข้อมูลจำเพาะต้องการคำชี้แจง: Bug # 6668279: (spec) System.gc () ควรระบุว่าเราไม่แนะนำให้ใช้และไม่รับประกันพฤติกรรม

RMI และ NIO ใช้เมธอด gc ภายในและต้องการการดำเนินการแบบซิงโครนัสซึ่ง: สิ่งนี้กำลังอยู่ในการอภิปราย:

จุดบกพร่อง # 5025281: อนุญาตให้ System.gc () เรียกใช้คอลเล็กชันแบบเต็มพร้อมกัน (ไม่ใช่หยุดโลก)


3

Garbage Collectionเป็นสิ่งที่ดีJavaถ้าเราเรียกใช้ซอฟต์แวร์ที่เข้ารหัสใน java ในเดสก์ท็อป / แล็ปท็อป / เซิร์ฟเวอร์ คุณสามารถโทรหาSystem.gc()หรือในRuntime.getRuntime().gc()Java

โปรดทราบว่าไม่มีการรับประกันการโทรเหล่านั้นว่าจะทำอะไร เป็นเพียงข้อเสนอแนะสำหรับ jvm เพื่อเรียกใช้ Garbage Collector มันขึ้นอยู่กับ JVM ว่ารัน GC หรือไม่ ดังนั้นคำตอบสั้น ๆ : เราไม่รู้ว่ามันทำงานเมื่อใด คำตอบที่ยาวขึ้น: JVM จะเรียกใช้ gc หากมีเวลาสำหรับสิ่งนั้น

ฉันเชื่อว่าเช่นเดียวกันกับ Android อย่างไรก็ตามอาจทำให้ระบบของคุณช้าลง


JVM จะเรียกใช้ gc หากมีเวลาสำหรับสิ่งนั้น : ไม่จริงเสมอไป Jvm สามารถเรียกใช้ gc ได้เมื่อหน่วยความจำ Eden เต็ม
abdelmouheimen

2

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

อย่างไรก็ตามฉันคิดว่าฉันเจอกรณีที่อาจเกี่ยวข้อง ฉันไม่แน่ใจเพราะฉันยังไม่ได้ทดสอบว่ามันมีผลหรือไม่:

เมื่อคุณแมปไฟล์หน่วยความจำฉันเชื่อว่าการเรียก map () จะพ่น IOException เมื่อบล็อกหน่วยความจำที่ใหญ่พอไม่พร้อมใช้งาน ฉันคิดว่าการรวบรวมขยะก่อนไฟล์ map () อาจช่วยป้องกันได้ คุณคิดอย่างไร?


2

เราไม่สามารถบังคับเก็บขยะได้ System.gc เป็นเพียงการแนะนำ vm สำหรับการรวบรวมขยะอย่างไรก็ตามกลไกทำงานเมื่อใดไม่มีใครรู้นี่เป็นไปตามข้อกำหนดของ JSR


2

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

ขณะนี้ฉันกำลังทำงานในโครงการที่เกี่ยวข้องกับสภาพแวดล้อมที่ จำกัด หน่วยความจำและข้อมูลจำนวนมากมีข้อมูลจำนวนมากที่ผลักดันสภาพแวดล้อมของฉันให้ถึงขีด จำกัด และแม้ว่าฉันจะสามารถลดการใช้หน่วยความจำลงได้ ตามทฤษฎีแล้วมันควรจะใช้งานได้ดี แต่ฉันก็ยังคงได้รับข้อผิดพลาดเกี่ยวกับพื้นที่ฮีป - ตัวเลือก GC แบบละเอียดแสดงให้ฉันเห็นว่ามันพยายามรวบรวมขยะ แต่ก็ไม่มีประโยชน์ ในดีบักเกอร์ฉันสามารถใช้ System.gc () ได้และแน่นอนว่าจะมีหน่วยความจำ "เหลือเฟือ" ที่พร้อมใช้งาน ...

ดังนั้นครั้งเดียวที่แอปพลิเคชันของฉันเรียก System.gc () คือเมื่อกำลังจะเข้าสู่ส่วนของรหัสซึ่งจะมีการจัดสรรบัฟเฟอร์ขนาดใหญ่ที่จำเป็นสำหรับการประมวลผลข้อมูลและการทดสอบหน่วยความจำว่างที่มีอยู่แสดงว่าฉันไม่ได้ รับประกันว่าจะมี โดยเฉพาะอย่างยิ่งฉันกำลังดูสภาพแวดล้อม 1gb ที่อย่างน้อย 300mb ถูกครอบครองโดยข้อมูลคงที่โดยข้อมูลที่ไม่คงที่จำนวนมากเกี่ยวข้องกับการดำเนินการยกเว้นเมื่อข้อมูลที่กำลังประมวลผลมีขนาดอย่างน้อย 100-200 MB ที่ แหล่งที่มา ทั้งหมดนี้เป็นส่วนหนึ่งของกระบวนการแปลงข้อมูลอัตโนมัติดังนั้นข้อมูลทั้งหมดจึงมีอยู่ในช่วงเวลาสั้น ๆ ในระยะยาว

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

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


2

หากคุณต้องการทราบว่าคุณSystem.gc()ถูกเรียกหรือไม่คุณสามารถด้วย Java 7 update 4 ใหม่รับการแจ้งเตือนเมื่อ JVM ดำเนินการ Garbage Collection

ฉันไม่แน่ใจ 100% ว่าคลาสGarbageCollectorMXBeanได้รับการแนะนำในการอัปเดต Java 7 4 เนื่องจากฉันไม่พบในบันทึกประจำรุ่น แต่ฉันพบข้อมูลในเว็บไซต์javaperformancetuning.com


0

ฉันไม่สามารถนึกถึงตัวอย่างที่เฉพาะเจาะจงได้เมื่อการเรียกใช้ GC ที่ชัดเจนจะเป็นการดี

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

อีกทางเลือกหนึ่งหากไปที่ฮีปด้วยตัววิเคราะห์ฮีปและคุณสงสัยว่าคอมโพเนนต์ไลบรารีเรียกใช้ GC ที่ชัดเจนคุณสามารถปิดการเพิ่ม: gc = -XX: + DisableExplicitGC ไปยังพารามิเตอร์ JVM


2
เราเรียก System.gc () เพื่อพยายามปิดอ็อบเจ็กต์ MappedByteBuffer ของเราซึ่งไม่ได้ปิดดังนั้นจึงไม่สามารถใช้งานได้สำหรับกระบวนการอื่น ๆ หรือสำหรับการตัดไฟล์แม้ว่าเราจะเรียก 'close ()' บนอ็อบเจ็กต์ น่าเสียดายที่มันยังไม่ปิดพวกเขาเสมอไป
Tim Cooper

0

accroding ที่จะคิดใน Java โดยบรูซ Eckel, กรณีการใช้งานหนึ่งสำหรับชัดเจนSystem.gc ()โทรคือเมื่อคุณต้องการที่จะมีผลบังคับใช้การสรุปคือการเรียกร้องให้เสร็จสิ้นวิธีการ


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

-1

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.