เหตุใดขนาดฮีปจึงถูกแก้ไขใน JVM


20

ใครสามารถอธิบายให้ฉันฟังได้ว่าทำไม JVM (ฉันไม่ได้ตรวจสอบมากเกินไป แต่ฉันไม่เคยเห็นใครที่ไม่ได้ทำแบบนั้น) จำเป็นต้องใช้ขนาดฮีปคงที่ ฉันรู้ว่ามันง่ายกว่าที่จะนำไปใช้กับกองซ้อนที่เรียบง่าย แต่ตอนนี้ Sun JVM มีอายุเกินหนึ่งทศวรรษแล้วดังนั้นฉันจึงคาดหวังให้พวกเขามีเวลาปรับปรุงสิ่งนี้

จำเป็นต้องกำหนดขนาดหน่วยความจำสูงสุดของโปรแกรมของคุณเมื่อเวลาเริ่มต้นดูเหมือนว่าสิ่งที่ต้องทำ 1960 และมีการโต้ตอบที่ไม่ดีกับการจัดการหน่วยความจำเสมือนของ OS (GC การดึงข้อมูลสลับไม่สามารถกำหนดจำนวนหน่วยความจำกระบวนการ Java ใช้งานจริงจากทางด้าน OS พื้นที่ VM จำนวนมากสูญเปล่า (ฉันรู้ว่าคุณไม่สนใจเครื่อง 48 บิตแฟนซีของคุณ ... )) ฉันยังเดาว่าความพยายามเศร้าต่าง ๆ ในการสร้างระบบปฏิบัติการขนาดเล็กภายใน JVM (EE แอ็พพลิเคชันเซิร์ฟเวอร์ OSGi) เป็นอย่างน้อยบางส่วนที่จะตำหนิในสถานการณ์เช่นนี้เพราะการเรียกใช้กระบวนการ Java หลายกระบวนการบนระบบนำไปสู่การสูญเสียทรัพยากร ให้หน่วยความจำแต่ละอันที่พวกเขาอาจต้องใช้สูงสุด

น่าแปลกใจที่ Google ไม่ได้ยอมแพ้กับความชั่วร้ายที่เกิดขึ้นกับสิ่งที่ฉันคาดหวัง แต่พวกเขาอาจถูกฝังอยู่ใต้ผู้คนหลายล้านคนเพื่อค้นหาขนาดกองที่มั่นคงและยอมรับความจริง


การออกแบบแอปพลิเคชันเซิร์ฟเวอร์ EE จะเหมาะสมอย่างสมบูรณ์แม้จะไม่มี "สถานการณ์" นี้เนื่องจาก JVM ต้องการพื้นที่และการสลับระหว่างเธรดมีราคาถูกกว่าการสลับระหว่างกระบวนการซึ่งเป็นหนึ่งในสิ่งที่ทำให้ Java มีขนาดใหญ่ในช่วงปลายยุค 90
Michael Borgwardt

นี่เป็นคำโวยวายและฉันสงสัยว่าคุณคิดมากแค่ไหน ตัวอย่างเช่นการโต้ตอบ GC / swap จะเปลี่ยนแปลงอย่างไรถ้าคุณไม่มีขีด จำกัด ฮีป พื้นที่ VM ถูกทำลายไปอย่างไร คุณได้รับพื้นที่ 2 / 3Gb ไม่ว่าคุณจะใช้หรือไม่ก็ตามและถ้าคุณเพิ่มขีด จำกัด ของพื้นที่นั้นมันไม่สำคัญว่าคุณจะได้รับกองที่คงที่หรือลอย สำหรับเรื่องนั้น JVM หลายรายการจะเสียสิ่งใดนอกจากการแลกเปลี่ยน (ซึ่งควรกำหนดค่าอย่างเหมาะสมสำหรับวัตถุประสงค์ของเครื่อง)
kdgregory

2
มันเป็นการพูดจาโผงผาง แต่ได้รับแจ้งจากประสบการณ์การเขียนประจำวันและการใช้งานแพลตฟอร์มบน Java ถ้าคุณไม่สลับ (เพราะมันจะทำให้ระบบของคุณไม่ตอบสนองเป็นเวลา 20 นาทีจนกระทั่งกระบวนการรันอะเวย์ไม่มีพื้นที่เหลือจัดสรร) และให้หน่วยความจำ overcommit ปิดใช้งานด้วยเหตุผลด้านเสถียรภาพ (OOM killer ไม่ค่อยดีในการเลือกเหยื่อ ) คุณสนใจพื้นที่ VM และตรงกันข้ามกับสิ่งที่ผู้คนด้านล่างกำลังหมายถึงการเปิดตัว Java VM พร้อมกับ -Xmx2048m จะจัดสรรหน่วยความจำเสมือน 2GB ทันที (อย่างน้อยใน Sun JVM บน Linux) สำหรับโปรแกรมที่มีตัวแปรหนึ่งตัว
themel

คำถามที่ยอดเยี่ยม เคยสงสัยในสิ่งเดียวกัน แต่ "ข้อเท็จจริง" ข้อใดที่นำเสนอที่นี่ในคิวและคำตอบนั้นถูกต้อง?
Martin Ba

สำหรับปฏิกิริยาที่คุณกำลังมองหาเพียงแค่ mosey พร้อมกับบั๊กดวงอาทิตย์ ... เช่นที่นี่ , ที่นี่และที่นี่ อ่านเหล่านั้นและรู้สึกถึงความโกรธแค้น :)
พื้นฐาน

คำตอบ:


23

คุณผิด. ขนาดฮีปของ JVM ไม่คงที่ จำกัด เพียงขอบเขต:

  • -Xmx ตั้งค่าขนาดหน่วยความจำฮีปสูงสุด
  • -Xms ตั้งค่าขนาดหน่วยความจำฮีพขั้นต่ำ

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


9
ไม่มีนาทีเป็นค่าเริ่มต้นเพื่อหลีกเลี่ยงการเริ่มต้นช้าเมื่อต้องจัดสรรจำนวนมากส่งผลให้เพิ่มฮีปซ้ำหลายครั้งเพจจิ้งจะจัดการกับแรมจริงหมด
ratchet freak

@ ratchetfreak นั่นเป็นการคาดเดาครั้งที่สองของฉัน ;-)
user281377

@ user281377 หากเป็นกรณีนี้ C # จะทำงานได้อย่างไรโดยไม่มีขนาดหน่วยความจำฮีปสูงสุด?
cmorse

cmorse: ฉันเดาได้เท่านั้น บางที Java มีการกำหนดเป้าหมายที่เซิร์ฟเวอร์ขนาดใหญ่ซึ่งมีหลายแอปพลิเคชันที่ใช้ทรัพยากรร่วมกันและมีการบังคับใช้ข้อ จำกัด อย่างเข้มงวดในขณะที่. net ทำขึ้นสำหรับพีซีและเซิร์ฟเวอร์ที่มีขนาดเล็กและมีความทุ่มเทมากกว่า
281377

@ user281377: แอปพลิเคชัน Java ที่ฉันใช้นั้นหมดพื้นที่ของฮีปโดยทั่วไปจะจัดการได้ไม่ดีนักโดยทั่วไปมักจะล้มเหลวหรือมีความผิดปกติมาก และ ASP.net ทำงานบนเซิร์ฟเวอร์ทั้งขนาดใหญ่และขนาดเล็กได้ดี สิ่งที่ฉันไม่ได้รับจริงๆคือทำไมโดยค่าเริ่มต้น Java บังคับใช้ขีด จำกัด นี้ ฉันชอบที่จะได้ยินจากเหตุผลเบื้องหลังการตัดสินใจของพวกเขา ... ฉันแน่ใจว่าพวกเขามีเหตุผลที่ดี
cmorse

6

ฉันคิดว่าคำตอบมีส่วนเกี่ยวข้องกับมรดกของ Java เดิมทีมันถูกออกแบบมาเป็นภาษาที่ใช้สำหรับระบบฝังตัวซึ่งมีข้อ จำกัด ของทรัพยากรอย่างชัดเจนและคุณไม่ต้องการให้กระบวนการต่าง ๆ จัดการกับสิ่งที่มีอยู่ นอกจากนี้ยังช่วยในการจัดการระบบเนื่องจากทำให้การจัดเตรียมทรัพยากรบนเซิร์ฟเวอร์ง่ายขึ้นหากคุณสามารถตั้งค่าขีด จำกัด ของทรัพยากรได้ ฉันเห็นว่า JVM ล่าสุดดูเหมือนจะใช้หลายฮีปแบบไม่ต่อเนื่องถึงแม้ว่าแน่นอนว่ามันทั้งหมดจะปรากฏเป็นฮีปเดียวสำหรับโค้ดของคุณ

(FWIW คุณต้องระบุข้อกำหนดหน่วยความจำของโปรแกรมภายใต้ MacOS รุ่น pre-Darwin ของ Mac [ถึงระบบ 7 ต่อไปซึ่งเป็นรุ่นล่าสุดที่ฉันใช้] ซึ่งเข้ากันได้ดีกับ 80s)


1
+1 - สำหรับการเป็น (1) คำตอบเดียวที่ตอบคำถามจริงและ (2) เป็นไปได้
kdgregory

2

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

กุญแจสำคัญคือการตั้งค่า ขอบเขตที่ถูกต้อง ฉันดู-msว่า "นี่เป็นจำนวนหน่วยความจำที่แอพของฉันต้องการ" และ-mx"มันไม่ควรเกินจำนวนนี้" ในการปรับใช้การผลิตทั้งสองควรอยู่ใกล้กันหากไม่เท่ากันและควรเป็นไปตามข้อกำหนดที่วัดได้จริง

ความกังวลของคุณเกี่ยวกับหน่วยความจำเสมือน "ที่สูญเปล่า" นั้นถูกใส่ผิดที่: เป็นเสมือนจริง (ฟรี) ใช่การจัดสรรฮีปให้ใหญ่เกินไปหมายความว่าคุณไม่สามารถเริ่มเธรดได้มากหรือโหลดไฟล์ที่แมปหน่วยความจำมากพอ แต่นั่นเป็นส่วนหนึ่งของการออกแบบแอพพลิเคชั่น: คุณมีทรัพยากรที่หายากคุณต้องแบ่งพาร์ติชั่นด้วยวิธีที่อนุญาตให้แอปพลิเคชันของคุณทำงานได้ ในฮีปแบบ "C-style" ซึ่งจะขยายจนกว่าคุณจะแตะที่ด้านบนสุดของหน่วยความจำปัญหาพื้นฐานก็เหมือนกันคุณแค่ไม่ต้องคิดถึงมันจนกว่าคุณจะมีปัญหา

สิ่งเดียวที่ฮีปขนาดใหญ่อาจ "เสีย" คือพื้นที่สว็อปเพราะเซ็กเมนต์ที่เขียนได้ทั้งหมดต้องมีข้อผูกมัดจากการแลกเปลี่ยน แต่นั่นเป็นส่วนหนึ่งของการออกแบบระบบ: ถ้าคุณต้องการให้ JVM จำนวนมากทำงานบนกล่องเดียวกันให้เพิ่มการสลับของคุณหรือลดการจัดสรรฮีป หากพวกเขาเริ่มการฟาดฟันแสดงว่าคุณกำลังพยายามทำมากเกินไปกับระบบ ซื้อหน่วยความจำเพิ่มเติม (และหากคุณยังใช้โปรเซสเซอร์ 32 บิตอยู่ให้ซื้อกล่องใหม่)


1
แต่เกณฑ์สำหรับการเรียกใช้ GC ไม่จำเป็นต้องเกี่ยวข้องกับเกณฑ์คงที่ที่หน่วยความจำทั้งหมดที่ใช้โดยโปรแกรมจะต้องไม่เกิน (ที่จริงแล้วมันไม่ควร; ถ้าคุณมีกองขนาดใหญ่และคุณสามารถทำ GC ได้เมื่อมันเต็มเท่านั้น ดูการรวบรวมขยะทั่วไป
Ben

@Ben - ใช่คุณพูดถูก และประโยคที่สองของฉันชี้ให้เห็นว่ามีทางเลือกอื่น อย่างไรก็ตามฉันไม่ยอมรับว่าฮีปขนาดคงที่เป็นวิธีที่ผิดในการจัดการ GC ในกรณีทั่วไป JVM ที่ปรับอย่างถูกต้องใช้ขนาดฮีป "ขวา"; ในประสบการณ์ของฉัน GC หยุดยาวเกิดขึ้นเมื่อ JVM ไม่ได้รับการปรับอย่างเหมาะสม และบ่อยครั้งที่ขนาดฮีป "ถูกต้อง" นั้นเล็กกว่าที่คุณคิด
kdgregory

-2

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

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

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