Stack, Static และ Heap ใน C ++


160

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

ฉันได้ยินมาว่าภาษาอื่น ๆ รวม "ผู้รวบรวมขยะ" ดังนั้นคุณไม่ต้องกังวลเกี่ยวกับความทรงจำ ที่เก็บขยะทำอะไร?

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

เมื่อมีคนพูดกับฉันว่าด้วยการประกาศนี้:

int * asafe=new int;

ฉันมี "ตัวชี้ไปยังตัวชี้" มันหมายความว่าอะไร? มันแตกต่างจาก:

asafe=new int;

?


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

มีความเป็นไปได้ที่ซ้ำซ้อนของกองอะไรและกองอยู่ที่ไหน
Swati Garg

คำตอบ:


223

มีการถามคำถามที่คล้ายกันแต่ไม่ได้ถามเกี่ยวกับสถิตยศาสตร์

สรุปหน่วยความจำสแตติกฮีปและสแต็กคือ:

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

  • ฮีปคือหน่วยความจำมากมายที่สามารถใช้งานได้แบบไดนามิก หากคุณต้องการ 4kb สำหรับวัตถุตัวจัดสรรแบบไดนามิกจะค้นหารายการของพื้นที่ว่างในฮีปให้เลือกอันขนาด 4kb และมอบให้กับคุณ โดยทั่วไปตัวจัดสรรหน่วยความจำแบบไดนามิก (malloc, new, et c.) จะเริ่มต้นที่ส่วนท้ายของหน่วยความจำและทำงานย้อนหลัง

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

เมื่อคุณต้องการที่จะใช้แต่ละคน:

  • Statics / globals มีประโยชน์สำหรับหน่วยความจำที่คุณรู้ว่าคุณต้องการเสมอและคุณรู้ว่าคุณไม่ต้องการยกเลิกการจัดสรร (โดยวิธีการที่สภาพแวดล้อมที่ฝังตัวอาจจะคิดว่ามีเพียงหน่วยความจำคงที่ ... สแต็คและกองเป็นส่วนหนึ่งของพื้นที่ที่อยู่ที่รู้จักร่วมกันโดยประเภทหน่วยความจำที่สาม: รหัสโปรแกรมโปรแกรมมักจะทำการจัดสรรแบบไดนามิกออกจากพวกเขา หน่วยความจำแบบสแตติกเมื่อพวกเขาต้องการสิ่งต่าง ๆ เช่นรายการที่เชื่อมโยง แต่ไม่ว่าหน่วยความจำแบบสแตติกเอง (บัฟเฟอร์) ไม่ใช่ตัวเอง "จัดสรร" แต่วัตถุอื่น ๆ จะถูกจัดสรรออกจากหน่วยความจำที่จัดเก็บโดยบัฟเฟอร์สำหรับวัตถุประสงค์นี้ ในแบบไม่ฝังเช่นกันและเกมคอนโซลมักจะหลีกเลี่ยงกลไกหน่วยความจำแบบไดนามิกที่สร้างขึ้นเพื่อควบคุมกระบวนการจัดสรรอย่างเข้มงวดโดยใช้บัฟเฟอร์ขนาดที่ตั้งไว้ล่วงหน้าสำหรับการจัดสรรทั้งหมด)

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

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

เก็บขยะ

ฉันได้ยินมาบ่อยมากเกี่ยวกับความสามารถในการเก็บขยะที่ดีเยี่ยมดังนั้นเสียงที่ไม่เห็นด้วยจะเป็นประโยชน์

การรวบรวมขยะเป็นกลไกที่ยอดเยี่ยมสำหรับเวลาที่ประสิทธิภาพไม่ใช่ปัญหาใหญ่ ฉันได้ยินว่า GCs เริ่มดีขึ้นและมีความซับซ้อนมากขึ้น แต่ความจริงก็คือคุณอาจถูกบังคับให้ยอมรับการปรับโทษ (ขึ้นอยู่กับการใช้งาน) และถ้าคุณขี้เกียจก็ยังอาจทำงานไม่ถูกต้อง ในช่วงเวลาที่ดีที่สุด Garbage Collector ตระหนักดีว่าหน่วยความจำของคุณจะหายไปเมื่อตระหนักว่าไม่มีการอ้างอิงอีกต่อไป (ดูการนับการอ้างอิง) แต่ถ้าคุณมีวัตถุที่อ้างถึงตัวเอง (อาจหมายถึงวัตถุอื่นซึ่งอ้างถึงกลับ) การอ้างอิงการนับเพียงอย่างเดียวจะไม่บ่งบอกว่าหน่วยความจำสามารถลบได้ ในกรณีนี้ GC ต้องดูซุปอ้างอิงทั้งหมดและดูว่ามีเกาะใดบ้างที่ถูกอ้างถึงด้วยตนเองเท่านั้น ฉันคิดว่ามันเป็นการใช้งาน O (n ^ 2) แต่ไม่ว่ามันจะเป็นอะไรมันจะแย่ถ้าคุณกังวลเกี่ยวกับการแสดง (แก้ไข: Martin B ชี้ให้เห็นว่ามันเป็น O (n) สำหรับอัลกอริทึมที่มีประสิทธิภาพพอสมควรซึ่งยังคงเป็น O (n) มากเกินไปถ้าคุณมีความกังวลเกี่ยวกับประสิทธิภาพและสามารถจัดสรรคืนในเวลาคงที่โดยไม่มีการรวบรวมขยะ)

โดยส่วนตัวเมื่อฉันได้ยินคนพูดว่า C ++ ไม่มีการรวบรวมขยะแท็กใจของฉันที่เป็นคุณลักษณะของ C ++ แต่ฉันอาจเป็นคนกลุ่มน้อย อาจเป็นสิ่งที่ยากที่สุดสำหรับคนที่จะเรียนรู้เกี่ยวกับการเขียนโปรแกรมใน C และ C ++ เป็นตัวชี้และวิธีจัดการการจัดสรรหน่วยความจำแบบไดนามิกอย่างถูกต้อง บางภาษาอื่น ๆ เช่น Python จะน่ากลัวหากไม่มี GC ดังนั้นฉันคิดว่าภาษานี้เป็นภาษาที่คุณต้องการ หากคุณต้องการประสิทธิภาพที่เชื่อถือได้แล้ว C ++ ที่ไม่มีการรวบรวมขยะเป็นสิ่งเดียวที่ Fortran ด้านนี้ที่ฉันนึกออก หากคุณต้องการความง่ายในการใช้งานและวงล้อฝึกอบรม (เพื่อช่วยคุณไม่ให้ล้มโดยไม่ต้องเรียนรู้การจัดการหน่วยความจำ "เหมาะสม") ให้เลือก GC แม้ว่าคุณจะรู้วิธีจัดการหน่วยความจำได้ดี แต่มันจะช่วยคุณประหยัดเวลาในการใช้รหัสอื่น ๆ ไม่มีโทษปรับประสิทธิภาพอีกต่อไป แต่ถ้าคุณต้องการประสิทธิภาพที่เชื่อถือได้ (และความสามารถในการรู้ว่าเกิดอะไรขึ้นเมื่ออยู่ภายใต้ฝาครอบ) ฉันจะใช้ C ++ มีเหตุผลที่เอ็นจิ้นเกมสำคัญ ๆ ทุกตัวที่ฉันเคยได้ยินมาคือ C ++ Python, et al นั้นดีสำหรับการเขียนสคริปต์ แต่ไม่ใช่เอ็นจิ้นเกมหลัก


มันไม่เกี่ยวข้องกับคำถามดั้งเดิม (จริง ๆ หรือมากไปกว่านั้น) แต่คุณมีที่ตั้งของกองซ้อนและกองหลัง โดยปกติกองเติบโตลงและกองโตขึ้น (แม้ว่ากองไม่จริง "เติบโต" ดังนั้นนี่คือเปลือกขนาดใหญ่) ...
P Daddy

ฉันไม่คิดว่าคำถามนี้เหมือนกันหรือซ้ำซ้อนกับคำถามอื่น อันนี้เป็นเรื่องเกี่ยวกับ C ++ โดยเฉพาะและสิ่งที่เขาหมายถึงคือระยะเวลาเก็บข้อมูลทั้งสามใน C ++ คุณสามารถจัดสรรวัตถุแบบไดนามิกในหน่วยความจำแบบคงที่ได้ดีเช่น overload op ใหม่
Johannes Schaub - litb

7
การรักษาความเสื่อมโทรมของคุณเกี่ยวกับการเก็บขยะน้อยกว่าประโยชน์เล็กน้อย
P พ่อ

9
บ่อยครั้งที่การรวบรวมขยะในปัจจุบันดีกว่าการเพิ่มหน่วยความจำด้วยตนเองเพราะมันเกิดขึ้นเมื่อมีงานเล็ก ๆ น้อย ๆ ซึ่งตรงข้ามกับการเพิ่มหน่วยความจำที่สามารถเกิดขึ้นได้เมื่อประสิทธิภาพสามารถนำไปใช้อย่างอื่นได้
Georg Schölly

3
แค่ความคิดเห็นเล็ก ๆ - การรวบรวมขยะไม่มีความซับซ้อน O (n ^ 2) (ซึ่งแน่นอนว่าจะเป็นผลเสียต่อประสิทธิภาพการทำงาน) เวลาที่รอบคอลเลกชันหนึ่งขยะเป็นสัดส่วนกับขนาดของกอง - ดูhpl.hp.com/personal/Hans_Boehm/gc/complexity.html
Martin B

54

แน่นอนว่าทั้งหมดต่อไปนี้ไม่ค่อยแม่นยำ นำติดตัวไปด้วยเม็ดเกลือเมื่อคุณอ่าน :)

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


ระยะเวลาการจัดเก็บอัตโนมัติ

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

if(some condition) {
    int a[3]; // array a has automatic storage duration
    fill_it(a);
    print_it(a);
}

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


ระยะเวลาการจัดเก็บแบบคงที่

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

// static storage duration. in global namespace scope
string globalA; 
int main() {
    foo();
    foo();
}

void foo() {
    // static storage duration. in local scope
    static string localA;
    localA += "ab"
    cout << localA;
}

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

class A {
    static string classScopeA;
};

string A::classScopeA;

A a, b; &a.classScopeA == &b.classScopeA == &A::classScopeA;

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


ระยะเวลาการจัดเก็บแบบไดนามิก

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

int main() {
    // the object that s points to has dynamic storage 
    // duration
    string *s = new string;
    // pass a pointer pointing to the object around. 
    // the object itself isn't touched
    foo(s);
    delete s;
}

void foo(string *s) {
    cout << s->size();
}

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

int main() {
    shared_ptr<string> s(new string);
    foo(s);
}

void foo(shared_ptr<string> s) {
    cout << s->size();
}

คุณไม่ต้องกังวลเกี่ยวกับการโทรลบ: ptr ที่ใช้ร่วมกันทำเพื่อคุณหากตัวชี้สุดท้ายที่อ้างอิงวัตถุนั้นอยู่นอกขอบเขต PTR ที่ใช้ร่วมกันของตัวเองมีระยะเวลาการจัดเก็บอัตโนมัติ ดังนั้นของอายุการใช้งานที่มีการจัดการโดยอัตโนมัติซึ่งจะช่วยให้ตรวจสอบว่าควรลบชี้ไปยังวัตถุแบบไดนามิกในเตาเผาของ สำหรับการอ้างอิง shared_ptr ดูที่เอกสารเพิ่ม: http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm


39

มีการพูดอย่างละเอียดเหมือน "คำตอบสั้น ๆ ":

  • ตัวแปรสแตติก (คลาส)
    อายุการใช้งาน = โปรแกรมรันไทม์ (1)
    การมองเห็น = กำหนดโดยตัวดัดแปลงการเข้าถึง (ส่วนตัว / ป้องกัน / สาธารณะ)

  • ตัวแปรคงที่ (ขอบเขตทั่วโลก)
    อายุการใช้งาน = โปรแกรมรันไทม์ (1)
    ทัศนวิสัย = หน่วยการรวบรวมที่มีอินสแตนซ์ใน (2)

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


  • การเปิดเผยตัวแปรสแต็ค = จากการประกาศจนกว่าจะออกจากขอบเขต
    ตลอดชีพ = จากการประกาศจนถึงการประกาศขอบเขตที่ออก


(1) มากกว่านี้: ตั้งแต่การเริ่มต้นจนกระทั่งการยกเลิกการย่อหน่วยของคอมไพล์ (เช่นไฟล์ C / C ++) ลำดับการเริ่มต้นของหน่วยการรวบรวมไม่ได้ถูกกำหนดโดยมาตรฐาน

(2) ระวัง: ถ้าคุณสร้างอินสแตนซ์ตัวแปรแบบคงที่ในส่วนหัวหน่วยการรวบรวมแต่ละหน่วยจะได้รับสำเนาของตัวเอง


5

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

ซ้อนกัน

จัดสรรได้เร็วกว่ามาก มันทำใน O (1) เนื่องจากถูกจัดสรรเมื่อตั้งค่ากรอบสแต็กดังนั้นจึงไม่มีความจำเป็น ข้อเสียเปรียบคือถ้าคุณหมดพื้นที่สแต็คคุณจะมีกระดูก คุณสามารถปรับขนาดสแต็กได้ แต่ IIRC คุณมี ~ 2MB เพื่อเล่น นอกจากนี้ทันทีที่คุณออกจากฟังก์ชั่นทุกอย่างในกองจะถูกล้าง ดังนั้นจึงอาจเป็นปัญหาในการอ้างถึงในภายหลัง (ตัวชี้ไปยังสแต็กการจัดสรรวัตถุนำไปสู่ข้อบกพร่อง)

กอง

ช้าลงอย่างมากในการจัดสรร แต่คุณมี GB ที่จะเล่นกับและชี้ไปที่

เก็บขยะ

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


3

ปัญหาของการเกิดไฟฟ้าสถิตย์และกองซ้อนคืออะไร?

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

ปัญหาเกี่ยวกับการจัดสรรใน "stack" คือการจัดสรรจะถูกทำลายทันทีที่รูทีนย่อยที่ส่งคืนการจัดสรร

ฉันสามารถเขียนแอปพลิเคชันทั้งหมดโดยไม่จัดสรรตัวแปรในฮีปหรือไม่

บางทีอาจจะไม่ใช่แอปพลิเคชันขนาดใหญ่ที่ไม่ธรรมดาธรรมดา (แต่เรียกว่า "โปรแกรมฝังตัว" อาจถูกเขียนโดยไม่มีฮีปโดยใช้ชุดย่อยของ C ++)

ตัวรวบรวมขยะทำอะไรได้บ้าง

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

ตัวรวบรวมขยะไม่ใช่คุณสมบัติปกติของการเขียนโปรแกรม C ++

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

เรียนรู้กลไก C ++ สำหรับการจัดสรรคืนหน่วยความจำที่กำหนดไว้:

  • 'คงที่': ไม่ถูกจัดสรรคืน
  • 'stack': ทันทีที่ตัวแปร "เกินขอบเขต"
  • 'ฮีป': เมื่อตัวชี้ถูกลบ (ลบโดยแอปพลิเคชันหรือลบอย่างชัดเจนภายในรูทีนย่อยบางส่วนหรืออื่น ๆ )

1

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

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


1

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


0

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

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

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

หากคุณพบเห็นการเขียนโปรแกรมเป็นครั้งแรกก่อนที่ GC จะแพร่หลายและคุ้นเคยกับ malloc / ฟรีและใหม่ / ลบมันอาจเป็นกรณีที่คุณพบว่า GC น่ารำคาญเล็กน้อยและ / หรือไม่ไว้ใจ (อาจเป็นเพราะ ' การเพิ่มประสิทธิภาพ 'ซึ่งมีประวัติที่ผ่านการตรวจสอบแล้ว) แอปจำนวนมากทนต่อเวลาแฝงแบบสุ่ม แต่สำหรับแอปที่ไม่มีการตอบสนองต่อการตอบสนองแบบสุ่มน้อยกว่านั้นคือการหลีกเลี่ยงสภาพแวดล้อม GC และย้ายไปในทิศทางของรหัสที่ไม่มีการจัดการ (หรือพระเจ้าห้ามศิลปะที่กำลังจะตายที่ยาวนานภาษาแอสเซมบลี)

ฉันมีนักเรียนภาคฤดูร้อนที่นี่สักพักเด็กฝึกงานคนฉลาดที่ถูกหย่านมใน GC; เขาเป็นคนที่น่าเกรงขามเกี่ยวกับความยิ่งใหญ่ของ GC แม้กระทั่งในขณะที่การเขียนโปรแกรมใน C / C ++ ที่ไม่มีการจัดการเขาปฏิเสธที่จะทำตามรูปแบบ malloc / ฟรี / ลบรูปแบบใหม่เพราะอ้างว่า "คุณไม่ควรทำสิ่งนี้ในภาษาโปรแกรม และคุณรู้? สำหรับแอปขนาดเล็กที่ใช้งานสั้นคุณสามารถหลีกเลี่ยงสิ่งนั้นได้ แต่ไม่ใช่สำหรับแอพที่ใช้งานได้นาน


0

Stack เป็นหน่วยความจำที่จัดสรรโดยคอมไพเลอร์เมื่อเรารวบรวมโปรแกรมในคอมไพเลอร์เริ่มต้นจะจัดสรรหน่วยความจำบางส่วนจาก OS (เราสามารถเปลี่ยนการตั้งค่าจากการตั้งค่าคอมไพเลอร์ใน IDE ของคุณ) และ OS เป็นหน่วยความจำของคุณ ในหน่วยความจำที่มีอยู่มากมายในระบบและสิ่งอื่น ๆ มากมายและการมาถึงหน่วยความจำสแต็กจะถูกจัดสรรเมื่อเราประกาศตัวแปรที่พวกเขาคัดลอก (อ้างอิงเป็น formals) ตัวแปรเหล่านั้นจะถูกผลักไปยังสแต็กตามแนวทางการตั้งชื่อตามค่าเริ่มต้น เช่นสัญกรณ์มัด: c = a + b; การผลักสแต็กเสร็จจากขวาไปซ้ายผลักดัน, ไปที่กองซ้อน, ตัวดำเนินการ, ไปยังสแต็คและผลลัพธ์ของ i เหล่านั้น, ไปยังสแต็ก ในสัญกรณ์การแก้ไขก่อน: = + cab นี่คือตัวแปรทั้งหมดจะถูกผลักไปยังสแต็กที่ 1 (จากขวาไปซ้าย) จากนั้นทำการดำเนินการ หน่วยความจำที่จัดสรรโดยคอมไพเลอร์ได้รับการแก้ไข ดังนั้นสมมติว่ามีการจัดสรรหน่วยความจำ 1MB ให้กับแอปพลิเคชันของเราสมมติว่าตัวแปรที่ใช้หน่วยความจำ 700kb (ตัวแปรโลคัลทั้งหมดถูกส่งไปยังสแต็กจนกว่าจะมีการจัดสรรแบบไดนามิก) ดังนั้นหน่วยความจำ 324kb ที่เหลือ และสแต็กนี้มีเวลาชีวิตน้อยลงเมื่อขอบเขตของฟังก์ชั่นสิ้นสุดลงสแต็คเหล่านี้จะถูกล้าง

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