Java Heap Allocation เร็วกว่า C ++


13

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


ฉันอ่านความคิดเห็นเกี่ยวกับคำตอบนี้และฉันเห็นข้อความนี้

การสร้างอินสแตนซ์ของวัตถุและคุณสมบัติเชิงวัตถุนั้นรวดเร็วในการใช้งาน (เร็วกว่า C ++ ในหลาย ๆ กรณี) เพราะพวกมันถูกออกแบบมาตั้งแต่ต้น และคอลเลกชันนั้นรวดเร็ว Java มาตรฐานชนะ C / C ++ มาตรฐานในพื้นที่นี้แม้สำหรับรหัส C ที่ปรับให้เหมาะสมที่สุด

ผู้ใช้หนึ่งราย (ที่มีตัวแทนที่สูงมากฉันอาจเพิ่ม) ปกป้องข้อเรียกร้องนี้อย่างกล้าหาญโดยระบุว่า

  1. การจัดสรรฮีปใน java ดีกว่าของ C ++

  2. และเพิ่มคำสั่งนี้เพื่อป้องกันการรวบรวมใน java

    และคอลเลกชัน Java นั้นเร็วกว่าคอลเลกชัน C ++ ซึ่งส่วนใหญ่จะเป็นระบบย่อยหน่วยความจำที่แตกต่างกัน

ดังนั้นคำถามของฉันอาจเป็นจริงได้และถ้าเป็นเช่นนั้นเหตุใดการจัดสรรฮีปของ Java จึงเร็วขึ้นมาก



1
มันเป็นเรื่องเล็กน้อย: ด้วย Java (หรือสภาพแวดล้อมที่มีการจัดการและ จำกัด อื่น ๆ ) คุณสามารถย้ายออบเจ็กต์และอัปเดตพอยน์เตอร์ให้กับพวกเขา - เช่นปรับให้เหมาะสมสำหรับตำแหน่งแคชที่ดีขึ้นแบบไดนามิก ด้วย C ++ และตัวคำนวณเลขคณิตพร้อมด้วยบิตแคสต์ที่ไม่สามารถควบคุมได้วัตถุทั้งหมดจะถูกตรึงไว้ที่ตำแหน่งตลอดไป
SK-logic

3
ฉันไม่เคยคิดเลยว่าจะได้ยินใครบางคนพูดว่าการจัดการหน่วยความจำ Java เร็วขึ้นเพราะมันเป็นการคัดลอกหน่วยความจำตลอดเวลา ถอนหายใจ
gbjbaanb

1
@gbjbaanb คุณเคยได้ยินเรื่องลำดับชั้นของหน่วยความจำบ้างไหม? แคชพลาดจุดโทษ? คุณทราบหรือไม่ว่าตัวจัดสรรวัตถุประสงค์ทั่วไปมีราคาแพงในขณะที่การจัดสรรรุ่นแรกเป็นเพียงการดำเนินการเพิ่มเติมเพียงครั้งเดียว
SK-logic

1
แม้ว่านี่อาจจะเป็นความจริงในบางกรณี แต่ก็ไม่ถึงจุดที่ใน java คุณจัดสรรทุกอย่างบนฮีปและใน c ++ คุณจัดสรรวัตถุจำนวนมากบนสแต็กซึ่งสามารถเร็วกว่ามาก
JohnB

คำตอบ:


23

นี่เป็นคำถามที่น่าสนใจและคำตอบนั้นซับซ้อน

โดยรวมแล้วฉันคิดว่ามันยุติธรรมที่จะบอกว่าตัวรวบรวมขยะ JVM ได้รับการออกแบบมาเป็นอย่างดีและมีประสิทธิภาพมาก มันอาจเป็นระบบการจัดการหน่วยความจำเอนกประสงค์ที่ดีที่สุด

C ++ สามารถเอาชนะ JVM GC ได้ด้วยตัวจัดสรรหน่วยความจำพิเศษที่ออกแบบมาเพื่อวัตถุประสงค์เฉพาะ ตัวอย่างอาจจะ:

  • ตัวจัดสรรหน่วยความจำต่อเฟรมซึ่งจะล้างพื้นที่หน่วยความจำทั้งหมดตามช่วงเวลา สิ่งเหล่านี้มักใช้ในเกม C ++ เช่นที่มีการใช้พื้นที่หน่วยความจำชั่วคราวหนึ่งครั้งต่อเฟรมและละทิ้งทันที
  • ตัวจัดสรรแบบกำหนดเองที่จัดการกลุ่มของวัตถุขนาดคงที่
  • การจัดสรรแบบกองซ้อน (แม้ว่าโปรดทราบว่า JVM ยังทำสิ่งนี้ในสถานการณ์ต่าง ๆ เช่นผ่านการวิเคราะห์แบบหลีกเลี่ยง )

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

การรวบรวมขยะยังช่วยให้คุณได้รับประโยชน์ที่สำคัญบางประการจากมุมมองของประสิทธิภาพ:

  • การสร้างอินสแตนซ์ของวัตถุนั้นเร็วมากจริงๆ เนื่องจากวิธีการที่วัตถุใหม่ได้รับการจัดสรรตามลำดับในหน่วยความจำมักจะต้องมีการเพิ่มตัวชี้มากกว่าหนึ่งตัวเล็กน้อยซึ่งเร็วกว่าอัลกอริธึมการจัดสรรฮีปทั่วไป C ++
  • คุณหลีกเลี่ยงความต้องการค่าใช้จ่ายในการจัดการวงจรชีวิต - เช่นการนับการอ้างอิง (บางครั้งใช้เป็นทางเลือกแทน GC) นั้นแย่มากจากมุมมองของประสิทธิภาพเนื่องจากการเพิ่มและลดจำนวนของการอ้างอิงบ่อยเพิ่มค่าใช้จ่ายจำนวนมาก .
  • หากคุณใช้วัตถุที่ไม่เปลี่ยนรูปคุณสามารถใช้ประโยชน์จากการแบ่งปันโครงสร้างเพื่อประหยัดหน่วยความจำและปรับปรุงประสิทธิภาพแคช สิ่งนี้ถูกใช้อย่างหนักโดยภาษาที่ใช้งานได้บน JVM เช่น Scala และ Clojure มันยากมากที่จะทำสิ่งนี้โดยไม่มี GC เพราะยากที่จะจัดการอายุการใช้งานของวัตถุที่ใช้ร่วมกัน หากคุณเชื่อ (อย่างที่ฉันทำ) ว่าความเปลี่ยนแปลงไม่ได้และการแบ่งปันโครงสร้างเป็นกุญแจสำคัญในการสร้างแอพพลิเคชั่นพร้อมกันขนาดใหญ่ดังนั้นนี่จึงเป็นข้อได้เปรียบด้านประสิทธิภาพที่ใหญ่ที่สุดของ GC
  • คุณสามารถหลีกเลี่ยงการคัดลอกหากวัตถุทุกประเภทและวงจรชีวิตที่เกี่ยวข้องถูกจัดการโดยระบบเก็บรวบรวมขยะแบบเดียวกัน ตรงกันข้ามกับ C ++ ซึ่งคุณมักจะต้องคัดลอกข้อมูลเต็มเพราะปลายทางต้องใช้วิธีการจัดการหน่วยความจำที่แตกต่างกันหรือมีวงจรชีวิตของวัตถุที่แตกต่างกัน

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


มีไลบรารีเฉพาะในพื้นที่ c ++ ที่แก้ไขปัญหานั้น ตัวอย่างที่มีชื่อเสียงที่สุดน่าจะเป็นสำหรับ SmartHeap
Tobias Langner

5
ซอฟท์เรียลไทม์ไม่ได้หมายความว่าคุณตกลงกำลังจะหยุดมักจะ นั่นหมายความว่าคุณสามารถหยุดชั่วคราว / ลองใหม่ในสถานการณ์ที่เลวร้ายจริง ๆมักจะไม่คาดคิดแทนที่จะหยุด / หยุดทำงาน / ล้มเหลว ไม่มีใครอยากจะใช้เครื่องเล่นเพลงหยุดชั่วคราว ปัญหาของการหยุด GC ชั่วคราวเกิดขึ้นบ่อยครั้งและคาดเดาไม่ได้ ในลักษณะเช่นนั้นการหยุด GC ชั่วคราวจะไม่สามารถยอมรับได้แม้จะเป็นการสมัครแบบเรียลไทม์ การหยุดชั่วคราว GC นั้นยอมรับได้ก็ต่อเมื่อผู้ใช้ไม่ใส่ใจคุณภาพของแอปพลิเคชัน และทุกวันนี้ผู้คนก็ไม่ได้ไร้เดียงสาอีกต่อไป
Eonil

1
โปรดโพสต์การวัดประสิทธิภาพเพื่อสนับสนุนการเรียกร้องของคุณมิฉะนั้นเราจะเปรียบเทียบแอปเปิ้ลและส้ม
JBRWilkinson

1
@Demetri แต่ในความเป็นจริงแล้วเฉพาะกรณีที่เกิดขึ้นมากเกินไป (และอีกครั้งแม้คาดเดาไม่ได้!) เว้นแต่คุณจะสามารถตอบสนองข้อ จำกัด บางอย่างที่ทำไม่ได้ กล่าวอีกนัยหนึ่ง C ++ นั้นง่ายกว่าสำหรับทุกสถานการณ์แบบเรียลไทม์
Eonil

1
เพื่อความสมบูรณ์: มีข้อเสียอีกอย่างหนึ่งของประสิทธิภาพการทำงานของ GC: ในหน่วยความจำที่เหลืออยู่ส่วนใหญ่เกิดขึ้นในเธรดอื่นซึ่งมีแนวโน้มว่าจะทำงานบนแกนที่แตกต่างกันนั่นหมายความว่า GCs เกิดต้นทุนแคชที่รุนแรง L1 / L2 แคชระหว่างแกนที่ต่างกัน นอกจากนี้บนเซิร์ฟเวอร์ที่มี NUMA ส่วนใหญ่แคช L3 จะต้องมีการทำข้อมูลให้ตรงกันด้วย (และมากกว่า Hypertransport / QPI, ouch (!))
ไม่มีข้อบกพร่องกระต่าย

3

นี่ไม่ใช่การอ้างสิทธิ์ทางวิทยาศาสตร์ ฉันแค่ให้อาหารเพื่อคิดในเรื่องนี้

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

คำตอบ: ม้วนพรมเก่า ๆ ทิ้งไป; แล้วพรมใหม่ออกมา

เราละเลยอะไรที่นี่?

  • ค่าใช้จ่ายในการเคลื่อนย้ายสิ่งของส่วนตัวที่มีอยู่แล้วย้ายเข้า
    • สิ่งนี้เรียกว่าต้นทุนการเก็บขยะแบบ "หยุดเพื่อโลก"
  • ค่าใช้จ่ายของพรมใหม่
    • ซึ่งบังเอิญสำหรับ RAM มันฟรี

การรวบรวมขยะเป็นหัวข้อใหญ่และมีคำถามมากมายทั้งใน Programmers.SE และ StackOverflow

ในประเด็นด้านผู้จัดการการจัดสรร C / C ++ ที่รู้จักกันในชื่อ TCMalloc พร้อมกับการนับการอ้างอิงวัตถุนั้นในทางทฤษฎีสามารถตอบสนองประสิทธิภาพที่ดีที่สุดของระบบ GC ใด ๆ


จริงๆแล้ว c ++ 11 ยังมีชุดเก็บขยะ ABIนี่มันค่อนข้างคล้ายกับคำตอบที่ฉันได้จาก SO
aaronman

มันเป็นความกลัวที่จะทำลายโปรแกรม C / C ++ ที่มีอยู่ (ฐานรหัสเช่นเคอร์เนล Linux และ archaic_but_still_economically_important library เช่น libtiff) ที่ขัดขวางความก้าวหน้าของนวัตกรรมภาษาใน C ++
ร.

ทำให้รู้สึกฉันจะเดาโดย c ++ 17 มันจะสมบูรณ์มากขึ้น แต่ความจริงก็คือเมื่อคุณเรียนรู้วิธีการเขียนโปรแกรมใน c ++ คุณไม่ต้องการมันอีกต่อไปบางทีพวกเขาอาจหาวิธีที่จะรวมสองสำนวน อย่างดี
aaronman

คุณรู้หรือไม่ว่ามีนักสะสมขยะที่ไม่หยุดยั้งโลก? คุณได้พิจารณาถึงผลกระทบของประสิทธิภาพการทำงานของการบีบอัดข้อมูล (บนฝั่ง GC) และการกระจายตัวของฮีป (สำหรับตัวจัดสรร C ++ ทั่วไป) หรือไม่
SK-logic

2
ฉันคิดว่าข้อบกพร่องหลักในการเปรียบเทียบนี้คือสิ่งที่ GC ทำจริง ๆ คือการหาเศษสกปรกตัดออกแล้วเห็นเศษที่เหลือกลับมารวมกันเพื่อสร้างพรมใหม่
svick

3

เหตุผลหลักคือเมื่อคุณขอ Java สำหรับก้อนหน่วยความจำใหม่มันจะไปที่ส่วนท้ายของกองและให้บล็อก ด้วยวิธีนี้การจัดสรรหน่วยความจำจะเร็วเท่ากับการจัดสรรบนสแต็ก (ซึ่งเป็นวิธีที่คุณใช้เวลาส่วนใหญ่ใน C / C ++ แต่นอกเหนือจากนั้น .. )

ดังนั้นการจัดสรรจะเร็วพอ ๆ กับอะไร แต่ ... ที่ไม่นับค่าใช้จ่ายในการเพิ่มหน่วยความจำ เพียงเพราะคุณไม่ปล่อยให้เป็นอิสระจนกระทั่งในภายหลังไม่ได้หมายความว่ามันไม่ได้มีค่าใช้จ่ายมากนักและในกรณีของระบบ GC ค่าใช้จ่ายนั้นค่อนข้างมากกว่าการจัดสรรฮีปแบบ 'ปกติ' - ไม่เพียง GC ต้องวิ่งผ่านวัตถุทั้งหมดเพื่อดูว่าพวกมันยังมีชีวิตอยู่หรือไม่จากนั้นก็ต้องปล่อยให้เป็นอิสระและคัดลอกหน่วยความจำไปรอบ ๆ เพื่อกระชับกอง - เพื่อให้คุณสามารถจัดสรรได้อย่างรวดเร็วในตอนท้าย กลไก (หรือคุณมีหน่วยความจำไม่เพียงพอตัวอย่างเช่น C / C ++ จะดำเนินการฮีปบนการจัดสรรทุกครั้งเพื่อค้นหาพื้นที่ว่างถัดไปที่สามารถพอดีกับวัตถุ)

นี่คือเหตุผลหนึ่งว่าทำไมมาตรฐาน Java / .NET แสดงประสิทธิภาพที่ดีเช่นนี้ แต่แอปพลิเคชันในโลกแห่งความจริงแสดงประสิทธิภาพที่ไม่ดี ฉันมีเพียงแค่ไปดูที่ปพลิเคชันบนโทรศัพท์มือถือของฉัน - จริงๆรวดเร็วตอบสนองคนที่กำลังทั้งหมดเขียนโดยใช้ NDK มากดังนั้นแม้ฉันรู้สึกประหลาดใจ

ทุกวันนี้คอลเลกชันสามารถเร็วถ้าวัตถุทั้งหมดได้รับการจัดสรรในพื้นที่เช่นในบล็อกที่ต่อเนื่องกัน ตอนนี้ใน Java คุณจะไม่ได้รับบล็อกที่ต่อเนื่องกันเนื่องจากวัตถุได้รับการจัดสรรครั้งละหนึ่งชิ้นจากจุดสิ้นสุดของฮีปที่ว่าง คุณสามารถจบลงด้วยการมีความสุขอย่างต่อเนื่อง แต่โชคดีเท่านั้น (เช่นลงไปจนถึงขั้นตอนการบดอัด GC และวิธีการคัดลอกวัตถุ) C / C ++ ในอีกทางหนึ่งสนับสนุนการจัดสรรที่ต่อเนื่อง (ผ่านสแต็กอย่างชัดเจน) โดยทั่วไปกองวัตถุใน C / C ++ ไม่แตกต่างจาก BTW ของ Java

ขณะนี้มี C / C ++ คุณสามารถดีกว่าตัวจัดสรรเริ่มต้นซึ่งออกแบบมาเพื่อประหยัดหน่วยความจำและใช้งานได้อย่างมีประสิทธิภาพ คุณสามารถแทนที่ตัวจัดสรรด้วยชุดของพูลบล็อกคงที่ดังนั้นคุณสามารถหาบล็อกที่มีขนาดที่เหมาะสมสำหรับวัตถุที่คุณกำลังจัดสรรอยู่เสมอ การเดินกองนั้นจะกลายเป็นเรื่องของการค้นหาบิตแมปเพื่อดูว่าบล็อกว่างอยู่ที่ไหนและการจัดสรรจะเป็นการตั้งค่าบิตในบิตแมปนั้นอีกครั้ง ค่าใช้จ่ายคือคุณใช้หน่วยความจำมากขึ้นเมื่อจัดสรรเป็นบล็อกขนาดคงที่ดังนั้นคุณจึงมีฮีปบล็อก 4 ไบต์และอีก 16 บล็อกไบต์เป็นต้น


2
ดูเหมือนว่าคุณไม่เข้าใจ GCS เลย พิจารณาสถานการณ์ที่พบบ่อยที่สุด - วัตถุขนาดเล็กหลายร้อยชิ้นได้รับการจัดสรรอย่างต่อเนื่อง แต่มีเพียงโหลเท่านั้นที่จะอยู่รอดได้นานกว่าหนึ่งวินาที วิธีนี้ไม่มีค่าใช้จ่ายใด ๆ ในการปลดปล่อยหน่วยความจำ - โหลนี้ถูกคัดลอกมาจากคนรุ่นใหม่ และโดยวิธีการที่น่าสงสาร Dalvik GC ไม่มีอะไรเกี่ยวข้องกับ GCs ที่ทันสมัยและทันสมัยคุณจะพบกับการใช้งาน JVM ที่เหมาะสม
SK-logic

1
หากหนึ่งในวัตถุที่ปล่อยออกมานั้นอยู่ตรงกลางของฮีปฮีปที่เหลือจะถูกกระชับเพื่อทำการเรียกคืนพื้นที่ หรือคุณกำลังบอกว่าการบดอัด GC ไม่ได้เกิดขึ้นเว้นแต่ว่าคุณจะอธิบายได้ดีที่สุด ฉันรู้ว่า GCs รุ่นทั่วไปทำได้ดีกว่ามากที่นี่เว้นแต่คุณจะปล่อยวัตถุในช่วงกลางของรุ่นต่อ ๆ ไปซึ่งในกรณีนี้ผลกระทบอาจมีขนาดค่อนข้างใหญ่ มีบางอย่างที่เขียนโดย Microsoftie ที่ทำงานกับ GC ของพวกเขาที่ฉันอ่านซึ่งอธิบายถึงการแลกเปลี่ยน GC เมื่อสร้าง GC generational ฉันจะดูว่าฉันสามารถหามันได้อีกครั้งหรือไม่
gbjbaanb

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

3
ฉันไม่เห็นด้วยกับการอ้างสิทธิ์ของคุณว่าการจัดสรรฮีปเป็น 'เร็วเท่ากับสแต็ก' - การจัดสรรฮีปต้องใช้การซิงโครไนซ์เธรดและสแต็กไม่ (ตามคำจำกัดความ)
JBRWilkinson

1
ฉันเดาเช่นนั้น แต่ด้วย Java และ. net คุณเห็นประเด็นของฉัน - คุณไม่ต้องเดินกองเพื่อค้นหาบล็อกฟรีถัดไปดังนั้นมันจึงเร็วขึ้นอย่างมากในเรื่องนั้น แต่ใช่ - คุณพูดถูก ล็อคซึ่งจะทำให้แอปพลิเคชันเธรดเสียหาย
gbjbaanb

2

อวกาศอีเด็น

ดังนั้นคำถามของฉันอาจเป็นจริงได้และถ้าเป็นเช่นนั้นเหตุใดการจัดสรรฮีปของ Java จึงเร็วขึ้นมาก

ฉันกำลังศึกษาอยู่เล็กน้อยเกี่ยวกับการทำงานของ Java GC เนื่องจากมันน่าสนใจสำหรับฉัน ฉันพยายามขยายคอลเลกชันของกลยุทธ์การจัดสรรหน่วยความจำใน C และ C ++ เสมอ (สนใจที่จะลองใช้สิ่งที่คล้ายกันใน C) และมันเป็นวิธีที่เร็วและรวดเร็วมากในการจัดสรรออบเจ็กต์จำนวนมากในเวลาอันรวดเร็ว มุมมองการปฏิบัติ แต่ส่วนใหญ่เนื่องจากการมัลติเธรด

วิธีการทำงานของการจัดสรร Java GC คือการใช้กลยุทธ์การจัดสรรที่ประหยัดที่สุดเพื่อเริ่มต้นการจัดสรรออบเจ็กต์ให้กับพื้นที่ "Eden" จากสิ่งที่ฉันสามารถบอกได้ก็คือการใช้ตัวจัดสรรพูลตามลำดับ

นั่นคือทั้งหมดที่เร็วขึ้นมากเพียงแค่ในแง่ของอัลกอริทึมและลดข้อผิดพลาดของหน้าบังคับกว่าวัตถุประสงค์ทั่วไปmallocใน C หรือเริ่มต้นโยนoperator newใน C ++

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

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

ชุด

ในตัวอย่างโครงสร้างข้อมูล / สระว่ายน้ำเรียงลำดับข้างต้นมันจะเป็นปัญหาใหญ่ถ้า Java GC จัดสรรด้วยวิธีนี้แม้ว่ามันจะเร็วสุดสำหรับการจัดสรรระเบิดของแต่ละชิ้นจำนวนมาก มันจะไม่สามารถปล่อยให้เป็นอิสระได้จนกว่าซอฟต์แวร์จะปิดตัวลงซึ่ง ณ จุดนั้นก็จะสามารถล้าง (ล้าง) หน่วยความจำทั้งหมดในคราวเดียวได้

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

โดยทั่วไปจะมีราคาค่อนข้างแพงดังนั้นจึงทำในเธรดพื้นหลังแยกเพื่อหลีกเลี่ยงการเธรดที่ค้างไว้ซึ่งเดิมจัดสรรหน่วยความจำทั้งหมด

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

ความเร็ว

ดังนั้นเหตุผลที่ Java GC อาจทำได้ดีกว่า C หรือ C ++ ที่การจัดสรรฮีปตรงเนื่องจากการใช้กลยุทธ์การจัดสรรที่ถูกที่สุดและเสื่อมโทรมที่สุดในเธรดที่ขอจัดสรรหน่วยความจำ จากนั้นมันจะบันทึกงานที่มีราคาแพงกว่าซึ่งปกติแล้วเราจะต้องทำเมื่อใช้ตัวจัดสรรทั่วไปเช่นเส้นตรงmallocสำหรับเธรดอื่น

ดังนั้นในความคิดจริงแล้ว GC ต้องทำงานโดยรวมให้มากขึ้น แต่มันกระจายไปทั่วเธรดเพื่อให้ต้นทุนเต็มไม่ได้จ่ายล่วงหน้าโดยเธรดเดี่ยว อนุญาตให้เธรดที่จัดสรรหน่วยความจำทำถูกสุด ๆ และจากนั้นเลื่อนการใช้จ่ายจริงที่จำเป็นในการทำสิ่งต่าง ๆ อย่างถูกต้องเพื่อให้แต่ละวัตถุสามารถถูกปล่อยให้เป็นเธรดอื่นได้อย่างแท้จริง ใน C หรือ C ++ เมื่อเราmallocหรือโทรoperator newเราจะต้องจ่ายค่าใช้จ่ายเต็มจำนวนล่วงหน้าภายในเธรดเดียวกัน

นี่คือความแตกต่างที่สำคัญและทำไม Java ถึงมีประสิทธิภาพสูงกว่า C หรือ C ++ โดยใช้การเรียกที่ไร้เดียงสาmallocหรือoperator newจัดสรรจำนวนชิ้นเล็ก ๆ ทีละรายการ แน่นอนว่าโดยทั่วไปจะมีการทำงานแบบปรมาณูและการล็อคที่เป็นไปได้เมื่อวงรอบ GC เริ่มเข้ามา

โดยทั่วไปคำอธิบายง่ายๆเดือดลงไปจ่ายเงินค่าใช้จ่ายหนักในหัวข้อเดียว ( malloc) กับการจ่ายเงินค่าใช้จ่ายที่ถูกกว่าในหัวข้อเดียวแล้วการจ่ายเงินค่าใช้จ่ายหนักในอีกที่สามารถทำงานในแบบคู่ขนาน ( GC) ในฐานะที่เป็นข้อเสียในการทำสิ่งต่าง ๆ ด้วยวิธีนี้หมายความว่าคุณต้องการสองทิศทางในการรับจากการอ้างอิงวัตถุไปยังวัตถุตามที่ต้องการเพื่อให้ผู้จัดสรรสามารถคัดลอก / ย้ายหน่วยความจำไปรอบ ๆ โดยไม่ทำให้การอ้างอิงวัตถุที่มีอยู่เป็นโมฆะ ย้ายออกจากพื้นที่ "Eden"

ท้ายสุด แต่ไม่ท้ายสุดการเปรียบเทียบนั้นค่อนข้างไม่ยุติธรรมเพราะรหัส C ++ ไม่ได้จัดสรรปริมาณของวัตถุเป็นจำนวนมากบนฮีป รหัส C ++ ที่ดีมีแนวโน้มที่จะจัดสรรหน่วยความจำสำหรับองค์ประกอบหลายอย่างในบล็อกที่ต่อเนื่องกันหรือบนสแต็ก หากจัดสรรจำนวนมากของวัตถุขนาดเล็กทีละตัวในร้านค้าฟรีรหัสนั้นจะเป็นสิ่งที่น่ารังเกียจ


0

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

หากคุณเพียงแค่ดูที่การจัดสรร / การจัดสรรคืนใน C ++ คุณอาจมีการโทรไปยัง malloc 1,000,000 ครั้งและโทรฟรี 1,000,000 ครั้ง () ใน Java คุณจะมีการโทร 1,000,000 ครั้งไปยัง new () และตัวรวบรวมขยะที่ทำงานอยู่ในลูปเพื่อค้นหาวัตถุ 1,000,000 รายการที่สามารถเพิ่มได้ฟรี การวนซ้ำอาจเร็วกว่าการโทรฟรี ()

ในทางกลับกัน malloc / free มีการปรับปรุงเวลาอื่น ๆ และโดยทั่วไปแล้ว malloc / free เพียงแค่ตั้งค่าหนึ่งบิตในโครงสร้างข้อมูลแยกต่างหากและได้รับการปรับให้เหมาะสมสำหรับ malloc / free ที่เกิดขึ้นในเธรดเดียวกันดังนั้นในสภาพแวดล้อมแบบมัลติเธรด มีการใช้ในหลายกรณี (และการล็อคหรือตัวแปรหน่วยความจำที่ใช้ร่วมกันมีราคาแพงมาก )

ในมือที่สามมีสิ่งต่าง ๆ เช่นการนับการอ้างอิงซึ่งคุณอาจต้องการโดยไม่มีการเก็บขยะและนั่นไม่ได้มาฟรี

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