ใน C malloc()
จัดสรรพื้นที่หน่วยความจำในฮีปและส่งคืนพอยน์เตอร์ให้กับมัน นั่นคือทั้งหมดที่คุณได้รับ หน่วยความจำนั้นไม่ได้กำหนดค่าเริ่มต้นและคุณไม่สามารถรับประกันได้ว่าจะเป็นศูนย์ทั้งหมดหรืออย่างอื่น
ใน Java การโทรnew
จะทำการจัดสรรโดยใช้ฮีปmalloc()
แต่คุณจะได้รับความสะดวกสบายเพิ่มขึ้นเช่นกัน (หรือค่าใช้จ่ายหากคุณต้องการ) ตัวอย่างเช่นคุณไม่จำเป็นต้องระบุจำนวนไบต์ที่จะจัดสรรอย่างชัดเจน คอมไพเลอร์คิดให้คุณตามประเภทของวัตถุที่คุณพยายามจัดสรร นอกจากนี้ตัวสร้างวัตถุจะถูกเรียก (ซึ่งคุณสามารถส่งผ่านข้อโต้แย้งไปยังหากคุณต้องการควบคุมวิธีการเริ่มต้นเกิดขึ้น) เมื่อnew
ส่งคืนคุณรับประกันว่าจะมีวัตถุที่เริ่มต้นได้
แต่ใช่ในตอนท้ายของการโทรทั้งผลลัพธ์malloc()
และnew
เป็นเพียงตัวชี้ไปยังข้อมูลบางส่วนของกอง
ส่วนที่สองของคำถามของคุณถามเกี่ยวกับความแตกต่างระหว่างกองและกอง คำตอบที่ครอบคลุมมากขึ้นสามารถพบได้โดยการออกแบบหลักสูตร (หรืออ่านหนังสือเกี่ยวกับ) คอมไพเลอร์ หลักสูตรเกี่ยวกับระบบปฏิบัติการก็มีประโยชน์เช่นกัน นอกจากนี้ยังมีคำถามและคำตอบมากมายเกี่ยวกับ SO เกี่ยวกับกองและกอง
ต้องบอกว่าฉันจะให้ภาพรวมทั่วไปฉันหวังว่าจะไม่ verbose มากเกินไปและมีวัตถุประสงค์เพื่ออธิบายความแตกต่างในระดับที่ค่อนข้างสูง
พื้นฐานเหตุผลหลักที่จะมีสองระบบการจัดการหน่วยความจำคือกองและกองสำหรับประสิทธิภาพ เหตุผลที่สองคือปัญหาแต่ละประเภทนั้นดีกว่าปัญหาประเภทอื่น
สแต็คค่อนข้างง่ายสำหรับฉันที่จะเข้าใจในแนวคิดดังนั้นฉันเริ่มต้นด้วยสแต็ค ลองพิจารณาฟังก์ชั่นนี้ใน C ...
int add(int lhs, int rhs) {
int result = lhs + rhs;
return result;
}
ดูเหมือนว่าข้างต้นค่อนข้างตรงไปตรง เรากำหนดฟังก์ชั่นชื่อadd()
และผ่านในการเพิ่มด้านซ้ายและขวา ฟังก์ชั่นเพิ่มพวกเขาและส่งกลับผลลัพธ์ โปรดเพิกเฉยสิ่งที่เป็นขอบทั้งหมดเช่นโอเวอร์โฟลว์ที่อาจเกิดขึ้น ณ จุดนี้มันไม่ได้มีประโยชน์ต่อการอภิปราย
add()
วัตถุประสงค์ของฟังก์ชันดูเหมือนตรงไปตรงสวย แต่สิ่งที่เราสามารถบอกเกี่ยวกับวงจรชีวิตของตนหรือไม่ โดยเฉพาะอย่างยิ่งการใช้งานหน่วยความจำความต้องการ?
สิ่งสำคัญที่สุดคือคอมไพเลอร์รู้เบื้องต้น (เช่นในเวลารวบรวม) ชนิดข้อมูลที่มีขนาดใหญ่และจำนวนที่จะใช้ lhs
และrhs
ข้อโต้แย้งที่มีsizeof(int)
4 ไบต์แต่ละ ตัวแปรresult
ก็เช่นsizeof(int)
กัน คอมไพเลอร์สามารถบอกได้ว่าadd()
ฟังก์ชั่นใช้4 bytes * 3 ints
หรือหน่วยความจำรวม 12 ไบต์
เมื่อadd()
ฟังก์ชั่นถูกเรียกลงทะเบียนฮาร์ดแวร์ที่เรียกว่าตัวชี้สแต็กจะมีที่อยู่ในนั้นที่ชี้ไปที่ด้านบนของสแต็ค ในการจัดสรรหน่วยความจำที่add()
จำเป็นต้องใช้ฟังก์ชั่นรหัสฟังก์ชั่นทั้งหมดที่ต้องทำคือการออกคำสั่งภาษาแอสเซมบลีหนึ่งเดียวเพื่อลดค่าตัวชี้สแต็กตัวชี้สแต็ค 12 โดยการทำเช่นนั้นints
แต่ละคนสำหรับlhs
, และrhs
result
การหาพื้นที่หน่วยความจำที่คุณต้องการโดยการดำเนินการคำสั่งเดียวนั้นเป็นชัยชนะที่ยิ่งใหญ่ในแง่ของความเร็วเพราะคำสั่งเดียวมักจะดำเนินการในหนึ่งนาฬิกาติ๊ก (1 พันล้านของวินาทีต่อวินาทีเป็น 1 GHz CPU)
นอกจากนี้จากมุมมองของคอมไพเลอร์ก็สามารถสร้างแผนที่ไปยังตัวแปรที่มีลักษณะที่น่ากลัวมากเช่นการจัดทำดัชนีอาร์เรย์:
lhs: ((int *)stack_pointer_register)[0]
rhs: ((int *)stack_pointer_register)[1]
result: ((int *)stack_pointer_register)[2]
ทั้งหมดนี้เร็วมาก
เมื่อadd()
ฟังก์ชั่นออกมามันจะต้องทำความสะอาด มันทำได้โดยการลบ 12 ไบต์จากการลงทะเบียนตัวชี้สแต็ก มันคล้ายกับการเรียกfree()
แต่ใช้เพียงหนึ่งคำสั่ง CPU และใช้เพียงหนึ่งเห็บ มันเร็วมาก ๆ
พิจารณาการจัดสรรแบบอิงฮีป สิ่งนี้เข้ามาเล่นเมื่อเราไม่ทราบว่าจะต้องใช้หน่วยความจำก่อนหน้าเท่าใด (เช่นเราจะเรียนรู้เฉพาะตอนรันไทม์)
พิจารณาฟังก์ชั่นนี้:
int addRandom(int count) {
int numberOfBytesToAllocate = sizeof(int) * count;
int *array = malloc(numberOfBytesToAllocate);
int result = 0;
if array != NULL {
for (i = 0; i < count; ++i) {
array[i] = (int) random();
result += array[i];
}
free(array);
}
return result;
}
ขอให้สังเกตว่าaddRandom()
ฟังก์ชั่นไม่ทราบว่าในเวลารวบรวมสิ่งที่ค่าของการcount
โต้แย้งจะเป็น ด้วยเหตุนี้มันจึงไม่สมเหตุสมผลที่จะพยายามกำหนดarray
อย่างที่เราต้องการหากเราวางมันลงบนสแต็กดังนี้:
int array[count];
หากcount
มีขนาดใหญ่อาจทำให้สแต็กของเรามีขนาดใหญ่เกินไปและเขียนทับส่วนโปรแกรมอื่น ๆ เมื่อเกิดการล้นสแต็กนี้เกิดขึ้นโปรแกรมของคุณขัดข้อง (หรือแย่กว่า)
malloc()
ดังนั้นในกรณีที่เราไม่ทราบว่าหน่วยความจำมากที่เราจะต้องจนรันไทม์ที่เราใช้ จากนั้นเราสามารถขอจำนวนไบต์ที่เราต้องการเมื่อเราต้องการและmalloc()
จะไปตรวจสอบว่าสามารถขายจำนวนไบต์ที่ ถ้าทำได้ดีเราได้รับมันคืนถ้าไม่เราจะได้ตัวชี้ NULL ที่บอกให้เราโทรmalloc()
ไม่สำเร็จ สะดุดตาแม้ว่าโปรแกรมจะไม่ผิดพลาด! แน่นอนว่าคุณในฐานะโปรแกรมเมอร์สามารถตัดสินใจว่าโปรแกรมของคุณไม่ได้รับอนุญาตให้ทำงานหากการจัดสรรทรัพยากรล้มเหลว แต่การเลิกจ้างที่เริ่มโดยโปรแกรมเมอร์นั้นแตกต่างจากความผิดพลาดปลอม
ดังนั้นตอนนี้เราต้องกลับมาดูที่ประสิทธิภาพ ตัวจัดสรรสแต็คนั้นเร็วมาก - หนึ่งคำสั่งในการจัดสรรหนึ่งคำสั่งสำหรับการจัดสรรคืนและดำเนินการโดยคอมไพเลอร์ แต่จำได้ว่าสแต็กนั้นมีความหมายสำหรับสิ่งต่าง ๆ เช่นตัวแปรโลคอลที่มีขนาดที่รู้จัก
ตัวจัดสรรฮีปในอีกทางหนึ่งมีขนาดของคำสั่งช้ากว่า จะต้องทำการค้นหาในตารางเพื่อดูว่ามีหน่วยความจำว่างเพียงพอที่จะสามารถขายจำนวนหน่วยความจำที่ผู้ใช้ต้องการ มีการปรับปรุงตารางเหล่านั้นหลังจากที่ vends หน่วยความจำเพื่อให้แน่ใจว่าไม่มีใครสามารถใช้บล็อกนั้น (การทำบัญชีนี้อาจต้องการให้ตัวจัดสรรเพื่อจองหน่วยความจำสำหรับตัวเองนอกเหนือจากสิ่งที่วางแผนจะขาย) ตัวจัดสรรต้องใช้กลยุทธ์การล็อกเพื่อให้แน่ใจว่าหน่วยความจำจะได้รับประโยชน์อย่างปลอดภัย และเมื่อหน่วยความจำในที่สุดfree()
d ซึ่งเกิดขึ้นในเวลาที่ต่างกันและโดยปกติแล้วไม่มีคำสั่งที่คาดเดาได้ผู้จัดสรรต้องค้นหาบล็อกที่ต่อเนื่องกันและต่อเข้าด้วยกันเพื่อซ่อมแซมการแยกส่วนของฮีป ถ้าฟังดูเหมือนว่าจะต้องใช้มากกว่าคำสั่ง CPU เพียงคำเดียวเพื่อให้บรรลุทั้งหมดคุณก็พูดถูก! มันซับซ้อนมากและใช้เวลาสักครู่
แต่กองใหญ่มาก มีขนาดใหญ่กว่ากองมาก เราสามารถได้รับความทรงจำมากมายจากพวกเขาและพวกเขาก็ยอดเยี่ยมเมื่อเราไม่รู้เวลารวบรวมจำนวนหน่วยความจำที่เราต้องการ ดังนั้นเราจึงแลกเปลี่ยนความเร็วสำหรับระบบหน่วยความจำที่มีการจัดการที่ปฏิเสธเราอย่างสุภาพแทนที่จะล้มเหลวเมื่อเราพยายามจัดสรรบางสิ่งที่ใหญ่เกินไป
ฉันหวังว่าจะช่วยตอบคำถามของคุณ โปรดแจ้งให้เราทราบหากคุณต้องการคำชี้แจงใด ๆ ข้างต้น