การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

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

การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

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

การจัดสรรสแต็ก

การจัดสรรหน่วยความจำแบบ Stack หมายถึงกระบวนการจัดสรรหน่วยความจำให้กับตัวแปรโลคอลและการเรียกฟังก์ชันในCall Stackกระบวนการนี้เกิดขึ้นโดยอัตโนมัติเมื่อมีการเรียกฟังก์ชัน และจะถูกปล่อยคืนทันทีเมื่อฟังก์ชันสิ้นสุดลง เนื่องจากระบบเป็นผู้จัดการหน่วยความจำ จึงรวดเร็วและมีประสิทธิภาพแต่มีพื้นที่จำกัดเมื่อเทียบกับการจัดสรรหน่วยความจำแบบ Heap หากมีการเรียกฟังก์ชันมากเกินไปจนเกินความจุของ Stack จะทำให้เกิด ข้อผิด พลาดStack Overflow

วิธีการทำงานของการจัดสรรสแต็ก

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

คุณสมบัติหลักของการจัดสรรสแต็ก

  • หน่วยความจำจะใช้งานได้เฉพาะขณะที่ฟังก์ชันกำลังทำงานอยู่เท่านั้น
  • การจัดสรรหน่วยความจำจะถูกยกเลิกโดยอัตโนมัติเมื่อฟังก์ชันสิ้นสุดลง
  • หากหน่วยความจำสแต็กเต็ม จะเกิด ข้อผิดพลาด เช่นJava.lang.StackOverflowErrorในภาษา Java และ segmentation fault ในภาษา C++
  • ปลอดภัยกว่าหน่วยความจำแบบฮีป เนื่องจากข้อมูลสามารถเข้าถึงได้โดยเธรดที่เป็นเจ้าของเท่านั้น
  • เร็วกว่าการจัดสรรหน่วยความจำแบบฮีป เนื่องจากมีการจัดการหน่วยความจำอัตโนมัติ

ตัวอย่างโค้ด:

int main() {
    
  // All these variables get memory
  // allocated on stack
  int a;
  int b[10];
  int n = 20;
  int c[n];
}

การจัดสรรฮีป

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

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

ประเภทของหน่วยความจำฮีป

หน่วยความจำฮีปถูกแบ่งออกเป็นสามประเภท ซึ่งช่วยในการจัดลำดับความสำคัญของการจัดเก็บวัตถุและการเก็บขยะ :

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

คุณลักษณะสำคัญของการจัดสรรฮีป

  • หากหน่วยความจำฮีปเต็ม JVM จะแสดงข้อผิดพลาด: java.lang.OutOfMemoryError
  • ต่างจากหน่วยความจำแบบสแต็ก หน่วยความจำแบบดิสคัฟเวอรี่จะไม่ถูกจัดสรรโดยอัตโนมัติจำเป็นต้องใช้ตัวเก็บขยะเพื่อปลดปล่อยหน่วยความจำที่ไม่ได้ใช้งาน
  • ช้ากว่าหน่วยความจำแบบ Stack เนื่องจากมี การจัดสรรหน่วยความจำ และการเก็บขยะด้วยตนเอง
  • มีความปลอดภัยต่อการทำงานแบบมัลติเธรดน้อยกว่าเนื่องจากหน่วยความจำฮีปถูกใช้ร่วมกันโดยทุกเธรด
  • โดยทั่วไปจะมีขนาดใหญ่กว่าเมื่อเทียบกับหน่วยความจำแบบสแต็ก
  • หน่วยความจำฮีปจะคงอยู่ตราบเท่าที่แอปพลิเคชันทั้งหมดกำลังทำงานอยู่

ตัวอย่างโค้ด:

int main()
{
   // This memory for 10 integers
   // is allocated on heap.
   int *ptr  = new int[10];
}

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

ด้านล่างนี้คือตัวอย่างการใช้งาน:

C++

#include <bits/stdc++.h>
using namespace std;

class Emp {
public:
    int id;
    string emp_name;

    // Constructor to initialize employee details
    Emp(int id, string emp_name) {
        this->id = id;
        this->emp_name = emp_name;
    }
};

// Function to create and return an Emp object
Emp Emp_detail(int id, string emp_name) {
    return Emp(id, emp_name);
}

int main() {
    // Initializing employee details
    int id = 21;
    string name = "Maddy";

    // Creating an Emp object using the function
    Emp person_ = Emp_detail(id, name);
    
    return 0;
}

Java

class Emp {
    int id;
    String emp_name;

    public Emp(int id, String emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

public class Emp_detail {
    private static Emp Emp_detail(int id, String emp_name) {
        return new Emp(id, emp_name);
    }

    public static void main(String[] args) {
        int id = 21;
        String name = "Maddy";
        Emp person_ = null;
        person_ = Emp_detail(id, name);
    }
}

Python

class Emp:
    # Constructor to initialize employee details
    def __init__(self, id, emp_name):
        self.id = id
        self.emp_name = emp_name

# Function to create and return an Emp object
def Emp_detail(id, emp_name):
    return Emp(id, emp_name)

if __name__ == "__main__":
    # Initializing employee details
    id = 21
    name = "Maddy"

    # Creating an Emp object using the function
    person_ = None
    person_ = Emp_detail(id, name)

JavaScript

class Emp {
    // Constructor to initialize employee details
    constructor(id, emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

// Function to create and return an Emp object
function Emp_detail(id, emp_name) {
    return new Emp(id, emp_name);
}

// Initializing employee details
let id = 21;
let name = "Maddy";

// Creating an Emp object using the function
let person_ = null;
person_ = Emp_detail(id, name);

จากตัวอย่างข้างต้น เราสามารถสรุปได้ดังนี้:

  • การจัดสรรหน่วยความจำฮีป:
    • เมื่อโปรแกรมเริ่มต้นทำงาน คลาสรันไทม์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป
  • การจัดสรรหน่วยความจำแบบ Stack:
    • เมธอดหลักจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก พร้อมกับตัวแปรโลคอลและตัวแปรอ้างอิงของมัน
    • ตัวแปรอ้างอิง Emp ชนิด Emp_detail ถูกเก็บไว้ในสแต็กและชี้ไปยังอ็อบเจ็กต์ที่เกี่ยวข้องในหน่วยความจำฮีป
  • การเรียกใช้ Constructor และการจัดสรรหน่วยความจำ :
    • คอนสตรัคเตอร์แบบมีพารามิเตอร์ Emp(int, string) จะถูกเรียกใช้จากฟังก์ชัน main และการทำงานของมันจะถูกจัดสรรไว้ที่ด้านบนสุดของสแต็ก
    • ภายในคอนสตรัคเตอร์: การอ้างอิงวัตถุจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก ตัวเลขจำนวนเต็มพื้นฐาน id จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก และการอ้างอิงสตริง emp_name จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก แต่จะชี้ไปยังสตริงจริงที่เก็บไว้ในพูลสตริง (หน่วยความจำแบบฮีป)
  • การเรียกใช้ฟังก์ชันและการจัดสรรหน่วยความจำ:
    • เมื่อมีการเรียก Emp_detail() จากฟังก์ชัน main ระบบจะสร้าง stack frame ใหม่ทับซ้อนกับ stack frame ก่อนหน้า
    • อ็อบเจ็กต์ Emp ที่สร้างขึ้นใหม่และตัวแปรอินสแตนซ์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป

ภาพประกอบแสดงดังแผนภาพด้านล่าง:

ความแตกต่างที่สำคัญระหว่างการจัดสรรหน่วยความจำแบบ Stack และ Heap

  1. ในโครงสร้างข้อมูลแบบสแต็ก การจัดสรรและการยกเลิกการจัดสรรจะ ดำเนินการ โดยอัตโนมัติโดยคอมไพเลอร์ ในขณะที่ในโครงสร้างข้อมูลแบบฮีป โปรแกรมเมอร์ต้องดำเนินการด้วยตนเอง
  2. การจัดการเฟรมฮีปนั้นมีค่าใช้จ่ายสูงกว่าการจัดการเฟรมสแต็ก
  3. ปัญหา การขาดแคลนหน่วยความจำมักเกิดขึ้นในหน่วยความจำแบบสแต็ก ในขณะที่ปัญหาหลักในหน่วยความจำแบบฮีปคือการแตกตัวของหน่วยความจำ (fragmentation)
  4. การเข้าถึงเฟรมสแต็ก นั้นง่าย กว่าเฟรมฮีป เนื่องจากสแต็กมีพื้นที่หน่วยความจำขนาดเล็กและเป็นมิตรกับแคชแต่ในกรณีของเฟรมฮีปซึ่งกระจายอยู่ทั่วหน่วยความจำ ทำให้เกิดแคชพลาด บ่อยกว่า
  5. โครงสร้างข้อมูลแบบ Stack นั้นไม่ยืดหยุ่นขนาดหน่วยความจำที่จัดสรรไว้ไม่สามารถเปลี่ยนแปลงได้ ในขณะที่โครงสร้างข้อมูลแบบ Heap นั้นยืดหยุ่นกว่า และขนาดหน่วยความจำที่จัดสรรไว้สามารถเปลี่ยนแปลง ได้
  6. การเข้าถึงเวลาของฮีปนั้นใช้เวลานานกว่าการเข้าถึงสแต็ก

ตารางเปรียบเทียบ

บทความที่เกี่ยวข้อง

การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

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

นักเขียนบทความ
by 
นักเขียนบทความ
การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

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

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

การจัดสรรสแต็ก

การจัดสรรหน่วยความจำแบบ Stack หมายถึงกระบวนการจัดสรรหน่วยความจำให้กับตัวแปรโลคอลและการเรียกฟังก์ชันในCall Stackกระบวนการนี้เกิดขึ้นโดยอัตโนมัติเมื่อมีการเรียกฟังก์ชัน และจะถูกปล่อยคืนทันทีเมื่อฟังก์ชันสิ้นสุดลง เนื่องจากระบบเป็นผู้จัดการหน่วยความจำ จึงรวดเร็วและมีประสิทธิภาพแต่มีพื้นที่จำกัดเมื่อเทียบกับการจัดสรรหน่วยความจำแบบ Heap หากมีการเรียกฟังก์ชันมากเกินไปจนเกินความจุของ Stack จะทำให้เกิด ข้อผิด พลาดStack Overflow

วิธีการทำงานของการจัดสรรสแต็ก

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

คุณสมบัติหลักของการจัดสรรสแต็ก

  • หน่วยความจำจะใช้งานได้เฉพาะขณะที่ฟังก์ชันกำลังทำงานอยู่เท่านั้น
  • การจัดสรรหน่วยความจำจะถูกยกเลิกโดยอัตโนมัติเมื่อฟังก์ชันสิ้นสุดลง
  • หากหน่วยความจำสแต็กเต็ม จะเกิด ข้อผิดพลาด เช่นJava.lang.StackOverflowErrorในภาษา Java และ segmentation fault ในภาษา C++
  • ปลอดภัยกว่าหน่วยความจำแบบฮีป เนื่องจากข้อมูลสามารถเข้าถึงได้โดยเธรดที่เป็นเจ้าของเท่านั้น
  • เร็วกว่าการจัดสรรหน่วยความจำแบบฮีป เนื่องจากมีการจัดการหน่วยความจำอัตโนมัติ

ตัวอย่างโค้ด:

int main() {
    
  // All these variables get memory
  // allocated on stack
  int a;
  int b[10];
  int n = 20;
  int c[n];
}

การจัดสรรฮีป

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

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

ประเภทของหน่วยความจำฮีป

หน่วยความจำฮีปถูกแบ่งออกเป็นสามประเภท ซึ่งช่วยในการจัดลำดับความสำคัญของการจัดเก็บวัตถุและการเก็บขยะ :

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

คุณลักษณะสำคัญของการจัดสรรฮีป

  • หากหน่วยความจำฮีปเต็ม JVM จะแสดงข้อผิดพลาด: java.lang.OutOfMemoryError
  • ต่างจากหน่วยความจำแบบสแต็ก หน่วยความจำแบบดิสคัฟเวอรี่จะไม่ถูกจัดสรรโดยอัตโนมัติจำเป็นต้องใช้ตัวเก็บขยะเพื่อปลดปล่อยหน่วยความจำที่ไม่ได้ใช้งาน
  • ช้ากว่าหน่วยความจำแบบ Stack เนื่องจากมี การจัดสรรหน่วยความจำ และการเก็บขยะด้วยตนเอง
  • มีความปลอดภัยต่อการทำงานแบบมัลติเธรดน้อยกว่าเนื่องจากหน่วยความจำฮีปถูกใช้ร่วมกันโดยทุกเธรด
  • โดยทั่วไปจะมีขนาดใหญ่กว่าเมื่อเทียบกับหน่วยความจำแบบสแต็ก
  • หน่วยความจำฮีปจะคงอยู่ตราบเท่าที่แอปพลิเคชันทั้งหมดกำลังทำงานอยู่

ตัวอย่างโค้ด:

int main()
{
   // This memory for 10 integers
   // is allocated on heap.
   int *ptr  = new int[10];
}

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

ด้านล่างนี้คือตัวอย่างการใช้งาน:

C++

#include <bits/stdc++.h>
using namespace std;

class Emp {
public:
    int id;
    string emp_name;

    // Constructor to initialize employee details
    Emp(int id, string emp_name) {
        this->id = id;
        this->emp_name = emp_name;
    }
};

// Function to create and return an Emp object
Emp Emp_detail(int id, string emp_name) {
    return Emp(id, emp_name);
}

int main() {
    // Initializing employee details
    int id = 21;
    string name = "Maddy";

    // Creating an Emp object using the function
    Emp person_ = Emp_detail(id, name);
    
    return 0;
}

Java

class Emp {
    int id;
    String emp_name;

    public Emp(int id, String emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

public class Emp_detail {
    private static Emp Emp_detail(int id, String emp_name) {
        return new Emp(id, emp_name);
    }

    public static void main(String[] args) {
        int id = 21;
        String name = "Maddy";
        Emp person_ = null;
        person_ = Emp_detail(id, name);
    }
}

Python

class Emp:
    # Constructor to initialize employee details
    def __init__(self, id, emp_name):
        self.id = id
        self.emp_name = emp_name

# Function to create and return an Emp object
def Emp_detail(id, emp_name):
    return Emp(id, emp_name)

if __name__ == "__main__":
    # Initializing employee details
    id = 21
    name = "Maddy"

    # Creating an Emp object using the function
    person_ = None
    person_ = Emp_detail(id, name)

JavaScript

class Emp {
    // Constructor to initialize employee details
    constructor(id, emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

// Function to create and return an Emp object
function Emp_detail(id, emp_name) {
    return new Emp(id, emp_name);
}

// Initializing employee details
let id = 21;
let name = "Maddy";

// Creating an Emp object using the function
let person_ = null;
person_ = Emp_detail(id, name);

จากตัวอย่างข้างต้น เราสามารถสรุปได้ดังนี้:

  • การจัดสรรหน่วยความจำฮีป:
    • เมื่อโปรแกรมเริ่มต้นทำงาน คลาสรันไทม์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป
  • การจัดสรรหน่วยความจำแบบ Stack:
    • เมธอดหลักจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก พร้อมกับตัวแปรโลคอลและตัวแปรอ้างอิงของมัน
    • ตัวแปรอ้างอิง Emp ชนิด Emp_detail ถูกเก็บไว้ในสแต็กและชี้ไปยังอ็อบเจ็กต์ที่เกี่ยวข้องในหน่วยความจำฮีป
  • การเรียกใช้ Constructor และการจัดสรรหน่วยความจำ :
    • คอนสตรัคเตอร์แบบมีพารามิเตอร์ Emp(int, string) จะถูกเรียกใช้จากฟังก์ชัน main และการทำงานของมันจะถูกจัดสรรไว้ที่ด้านบนสุดของสแต็ก
    • ภายในคอนสตรัคเตอร์: การอ้างอิงวัตถุจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก ตัวเลขจำนวนเต็มพื้นฐาน id จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก และการอ้างอิงสตริง emp_name จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก แต่จะชี้ไปยังสตริงจริงที่เก็บไว้ในพูลสตริง (หน่วยความจำแบบฮีป)
  • การเรียกใช้ฟังก์ชันและการจัดสรรหน่วยความจำ:
    • เมื่อมีการเรียก Emp_detail() จากฟังก์ชัน main ระบบจะสร้าง stack frame ใหม่ทับซ้อนกับ stack frame ก่อนหน้า
    • อ็อบเจ็กต์ Emp ที่สร้างขึ้นใหม่และตัวแปรอินสแตนซ์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป

ภาพประกอบแสดงดังแผนภาพด้านล่าง:

ความแตกต่างที่สำคัญระหว่างการจัดสรรหน่วยความจำแบบ Stack และ Heap

  1. ในโครงสร้างข้อมูลแบบสแต็ก การจัดสรรและการยกเลิกการจัดสรรจะ ดำเนินการ โดยอัตโนมัติโดยคอมไพเลอร์ ในขณะที่ในโครงสร้างข้อมูลแบบฮีป โปรแกรมเมอร์ต้องดำเนินการด้วยตนเอง
  2. การจัดการเฟรมฮีปนั้นมีค่าใช้จ่ายสูงกว่าการจัดการเฟรมสแต็ก
  3. ปัญหา การขาดแคลนหน่วยความจำมักเกิดขึ้นในหน่วยความจำแบบสแต็ก ในขณะที่ปัญหาหลักในหน่วยความจำแบบฮีปคือการแตกตัวของหน่วยความจำ (fragmentation)
  4. การเข้าถึงเฟรมสแต็ก นั้นง่าย กว่าเฟรมฮีป เนื่องจากสแต็กมีพื้นที่หน่วยความจำขนาดเล็กและเป็นมิตรกับแคชแต่ในกรณีของเฟรมฮีปซึ่งกระจายอยู่ทั่วหน่วยความจำ ทำให้เกิดแคชพลาด บ่อยกว่า
  5. โครงสร้างข้อมูลแบบ Stack นั้นไม่ยืดหยุ่นขนาดหน่วยความจำที่จัดสรรไว้ไม่สามารถเปลี่ยนแปลงได้ ในขณะที่โครงสร้างข้อมูลแบบ Heap นั้นยืดหยุ่นกว่า และขนาดหน่วยความจำที่จัดสรรไว้สามารถเปลี่ยนแปลง ได้
  6. การเข้าถึงเวลาของฮีปนั้นใช้เวลานานกว่าการเข้าถึงสแต็ก

ตารางเปรียบเทียบ

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.

บทความที่เกี่ยวข้อง

การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

การจัดสรรหน่วยความจำแบบสแต็กเทียบกับฮีป

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

Lorem ipsum dolor amet consectetur adipiscing elit tortor massa arcu non.

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

การจัดสรรสแต็ก

การจัดสรรหน่วยความจำแบบ Stack หมายถึงกระบวนการจัดสรรหน่วยความจำให้กับตัวแปรโลคอลและการเรียกฟังก์ชันในCall Stackกระบวนการนี้เกิดขึ้นโดยอัตโนมัติเมื่อมีการเรียกฟังก์ชัน และจะถูกปล่อยคืนทันทีเมื่อฟังก์ชันสิ้นสุดลง เนื่องจากระบบเป็นผู้จัดการหน่วยความจำ จึงรวดเร็วและมีประสิทธิภาพแต่มีพื้นที่จำกัดเมื่อเทียบกับการจัดสรรหน่วยความจำแบบ Heap หากมีการเรียกฟังก์ชันมากเกินไปจนเกินความจุของ Stack จะทำให้เกิด ข้อผิด พลาดStack Overflow

วิธีการทำงานของการจัดสรรสแต็ก

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

คุณสมบัติหลักของการจัดสรรสแต็ก

  • หน่วยความจำจะใช้งานได้เฉพาะขณะที่ฟังก์ชันกำลังทำงานอยู่เท่านั้น
  • การจัดสรรหน่วยความจำจะถูกยกเลิกโดยอัตโนมัติเมื่อฟังก์ชันสิ้นสุดลง
  • หากหน่วยความจำสแต็กเต็ม จะเกิด ข้อผิดพลาด เช่นJava.lang.StackOverflowErrorในภาษา Java และ segmentation fault ในภาษา C++
  • ปลอดภัยกว่าหน่วยความจำแบบฮีป เนื่องจากข้อมูลสามารถเข้าถึงได้โดยเธรดที่เป็นเจ้าของเท่านั้น
  • เร็วกว่าการจัดสรรหน่วยความจำแบบฮีป เนื่องจากมีการจัดการหน่วยความจำอัตโนมัติ

ตัวอย่างโค้ด:

int main() {
    
  // All these variables get memory
  // allocated on stack
  int a;
  int b[10];
  int n = 20;
  int c[n];
}

การจัดสรรฮีป

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

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

ประเภทของหน่วยความจำฮีป

หน่วยความจำฮีปถูกแบ่งออกเป็นสามประเภท ซึ่งช่วยในการจัดลำดับความสำคัญของการจัดเก็บวัตถุและการเก็บขยะ :

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

คุณลักษณะสำคัญของการจัดสรรฮีป

  • หากหน่วยความจำฮีปเต็ม JVM จะแสดงข้อผิดพลาด: java.lang.OutOfMemoryError
  • ต่างจากหน่วยความจำแบบสแต็ก หน่วยความจำแบบดิสคัฟเวอรี่จะไม่ถูกจัดสรรโดยอัตโนมัติจำเป็นต้องใช้ตัวเก็บขยะเพื่อปลดปล่อยหน่วยความจำที่ไม่ได้ใช้งาน
  • ช้ากว่าหน่วยความจำแบบ Stack เนื่องจากมี การจัดสรรหน่วยความจำ และการเก็บขยะด้วยตนเอง
  • มีความปลอดภัยต่อการทำงานแบบมัลติเธรดน้อยกว่าเนื่องจากหน่วยความจำฮีปถูกใช้ร่วมกันโดยทุกเธรด
  • โดยทั่วไปจะมีขนาดใหญ่กว่าเมื่อเทียบกับหน่วยความจำแบบสแต็ก
  • หน่วยความจำฮีปจะคงอยู่ตราบเท่าที่แอปพลิเคชันทั้งหมดกำลังทำงานอยู่

ตัวอย่างโค้ด:

int main()
{
   // This memory for 10 integers
   // is allocated on heap.
   int *ptr  = new int[10];
}

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

ด้านล่างนี้คือตัวอย่างการใช้งาน:

C++

#include <bits/stdc++.h>
using namespace std;

class Emp {
public:
    int id;
    string emp_name;

    // Constructor to initialize employee details
    Emp(int id, string emp_name) {
        this->id = id;
        this->emp_name = emp_name;
    }
};

// Function to create and return an Emp object
Emp Emp_detail(int id, string emp_name) {
    return Emp(id, emp_name);
}

int main() {
    // Initializing employee details
    int id = 21;
    string name = "Maddy";

    // Creating an Emp object using the function
    Emp person_ = Emp_detail(id, name);
    
    return 0;
}

Java

class Emp {
    int id;
    String emp_name;

    public Emp(int id, String emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

public class Emp_detail {
    private static Emp Emp_detail(int id, String emp_name) {
        return new Emp(id, emp_name);
    }

    public static void main(String[] args) {
        int id = 21;
        String name = "Maddy";
        Emp person_ = null;
        person_ = Emp_detail(id, name);
    }
}

Python

class Emp:
    # Constructor to initialize employee details
    def __init__(self, id, emp_name):
        self.id = id
        self.emp_name = emp_name

# Function to create and return an Emp object
def Emp_detail(id, emp_name):
    return Emp(id, emp_name)

if __name__ == "__main__":
    # Initializing employee details
    id = 21
    name = "Maddy"

    # Creating an Emp object using the function
    person_ = None
    person_ = Emp_detail(id, name)

JavaScript

class Emp {
    // Constructor to initialize employee details
    constructor(id, emp_name) {
        this.id = id;
        this.emp_name = emp_name;
    }
}

// Function to create and return an Emp object
function Emp_detail(id, emp_name) {
    return new Emp(id, emp_name);
}

// Initializing employee details
let id = 21;
let name = "Maddy";

// Creating an Emp object using the function
let person_ = null;
person_ = Emp_detail(id, name);

จากตัวอย่างข้างต้น เราสามารถสรุปได้ดังนี้:

  • การจัดสรรหน่วยความจำฮีป:
    • เมื่อโปรแกรมเริ่มต้นทำงาน คลาสรันไทม์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป
  • การจัดสรรหน่วยความจำแบบ Stack:
    • เมธอดหลักจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก พร้อมกับตัวแปรโลคอลและตัวแปรอ้างอิงของมัน
    • ตัวแปรอ้างอิง Emp ชนิด Emp_detail ถูกเก็บไว้ในสแต็กและชี้ไปยังอ็อบเจ็กต์ที่เกี่ยวข้องในหน่วยความจำฮีป
  • การเรียกใช้ Constructor และการจัดสรรหน่วยความจำ :
    • คอนสตรัคเตอร์แบบมีพารามิเตอร์ Emp(int, string) จะถูกเรียกใช้จากฟังก์ชัน main และการทำงานของมันจะถูกจัดสรรไว้ที่ด้านบนสุดของสแต็ก
    • ภายในคอนสตรัคเตอร์: การอ้างอิงวัตถุจะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก ตัวเลขจำนวนเต็มพื้นฐาน id จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก และการอ้างอิงสตริง emp_name จะถูกเก็บไว้ในหน่วยความจำแบบสแต็ก แต่จะชี้ไปยังสตริงจริงที่เก็บไว้ในพูลสตริง (หน่วยความจำแบบฮีป)
  • การเรียกใช้ฟังก์ชันและการจัดสรรหน่วยความจำ:
    • เมื่อมีการเรียก Emp_detail() จากฟังก์ชัน main ระบบจะสร้าง stack frame ใหม่ทับซ้อนกับ stack frame ก่อนหน้า
    • อ็อบเจ็กต์ Emp ที่สร้างขึ้นใหม่และตัวแปรอินสแตนซ์ทั้งหมดจะถูกจัดเก็บไว้ในหน่วยความจำฮีป

ภาพประกอบแสดงดังแผนภาพด้านล่าง:

ความแตกต่างที่สำคัญระหว่างการจัดสรรหน่วยความจำแบบ Stack และ Heap

  1. ในโครงสร้างข้อมูลแบบสแต็ก การจัดสรรและการยกเลิกการจัดสรรจะ ดำเนินการ โดยอัตโนมัติโดยคอมไพเลอร์ ในขณะที่ในโครงสร้างข้อมูลแบบฮีป โปรแกรมเมอร์ต้องดำเนินการด้วยตนเอง
  2. การจัดการเฟรมฮีปนั้นมีค่าใช้จ่ายสูงกว่าการจัดการเฟรมสแต็ก
  3. ปัญหา การขาดแคลนหน่วยความจำมักเกิดขึ้นในหน่วยความจำแบบสแต็ก ในขณะที่ปัญหาหลักในหน่วยความจำแบบฮีปคือการแตกตัวของหน่วยความจำ (fragmentation)
  4. การเข้าถึงเฟรมสแต็ก นั้นง่าย กว่าเฟรมฮีป เนื่องจากสแต็กมีพื้นที่หน่วยความจำขนาดเล็กและเป็นมิตรกับแคชแต่ในกรณีของเฟรมฮีปซึ่งกระจายอยู่ทั่วหน่วยความจำ ทำให้เกิดแคชพลาด บ่อยกว่า
  5. โครงสร้างข้อมูลแบบ Stack นั้นไม่ยืดหยุ่นขนาดหน่วยความจำที่จัดสรรไว้ไม่สามารถเปลี่ยนแปลงได้ ในขณะที่โครงสร้างข้อมูลแบบ Heap นั้นยืดหยุ่นกว่า และขนาดหน่วยความจำที่จัดสรรไว้สามารถเปลี่ยนแปลง ได้
  6. การเข้าถึงเวลาของฮีปนั้นใช้เวลานานกว่าการเข้าถึงสแต็ก

ตารางเปรียบเทียบ

Related articles