Heap Và Stack Khác Biệt Đến Như Thế Nào?

Trong lập trình, ngoài việc viết code sao cho đẹp và tối ưu, bên cạnh đó còn một vấn đề khá nhức nhối chính là quản lý bộ nhớ. Đôi khi chỉ vì quên giải phóng một “em nào đó” mà dẫn đến cả chương trình bị “đơ” do tràn bộ nhớ. Bài viết dưới đây giúp cho các bạn có cái nhìn rõ hơn trong việc phân cấp và sử dụng bộ nhớ để lưu trữ.

Bộ nhớ trong lập trình?

Quản lý bộ nhớ là một vấn đề khó khăn với một lập trình viên. Việc làm ra được một phần mềm hoặc một web cần phải biết cách quản lý hoặc sử dụng bộ nhớ tối ưu và hiệu quả, nếu không sẽ dẫn đến tình trạng tràn bộ nhớ. 

Khi khởi tạo một chương trình, chương trình đó sẽ yêu cầu máy tính cấp phát các vùng nhớ sử dụng để lưu trữ dữ liệu khi đang thực thi.

Bộ nhớ trong máy hay trong chương trình không thể sử dụng một cách tùy tiện, mà phải qua sự quản lý chặt chẽ để có thể:

  • Tận dụng tối đa các vùng nhớ trống để có thể đạt năng suất cao
  • Giải phóng các vùng nhớ không sử dụng nữa để tránh gây lãng phí

Tùy vào một số chương trình khác nhau sẽ có các cách lưu trữ và quản lý khác nhau. Nhưng nhìn chung các chương trình sẽ lưu trữ dữ liệu trên các vùng nhớ được gọi là Heap và Stack.

So sánh Heap và Stack

Cả 2 vùng nhớ Heap và Stack đều được tạo ra và lưu trữ trong RAM khi chương trình được thực thi.

1. Cách thức lưu trữ

Bộ nhớ Stack được dùng để lưu trữ các biến cục bộ (local variable) bên trong hàm, vùng nhớ này dùng để chứa giá trị của các tham số khi được gọi đến. Khi kết thúc một hàm, vùng nhớ Stack sẽ được tự động giải phóng. Stack sử dụng theo cấu trúc LIFO (vào sau ra trước)

Bộ nhớ Heap được dùng để lưu trữ các biến toàn cục (global variable) của chương trình, hoặc lưu trữ các đối tượng của con trỏ khi được cấp phát. Vùng nhớ Heap phải được giải phóng thông qua hàm bởi lập trình viên (tuy nhiên ở 1 số ngôn ngữ hiện nay có chức năng tự động thu gom, giải phóng vùng nhớ như GC trong C#).

void main() 
{
    int *a = null;            //Tạo trên vùng nhớ Stack
    int b;                    //Tạo trên vùng nhớ Stack
    {
         int c = 5;           //Tạo trên vùng nhớ Stack
         *a = new int[100];   //Tạo mảng 500 biến int trên vùng nhớ Heap
    }                         //Giải phóng c nhưng không giải phóng vùng nhớ mà a trỏ tới
    delete[] a;               //Giải phóng mảng int mà a trỏ tới
}                             //Giải phóng a và b

2. Kích thước


Kích thước Stack nhỏ hơn Heap


Việc khởi tạo giá trị trên vùng nhớ của Stack cần phải quan tâm rất nhiều tới vấn đề cấp phát, do chương trình chỉ cung cấp một kích thước nhất định cho vùng nhớ này. Trong khi đó vùng nhớ Heap lớn hơn và có thể xin cấp phát thêm từ bộ nhớ của máy tính. 

Lưu ý: Khi vượt quá khả năng lưu trữ, chương trình sẽ bị tràn bộ nhớ (stack overflow).

void Infinity() 
{
     int a;       //Tạo vùng nhớ Stack
     Infinity();  //Đệ quy lại hàm này
}                 //Sử dụng hết vùng nhớ Stack do không giải phóng được

Lưu ý: Heap có thể gặp lỗi khi cấp phát bộ đệm quá lớn

void main() 
{
    int *a = (int *) malloc(10000000000000000000000000); //Lỗi khởi tạo vùng nhớ quá lớn
    return 0;
}

3. Tốc độ truy xuất


Tốc độ truy xuất Stack nhanh và được ưu tiên hơn Heap


Tuy nhiên việc truy xuất đến bộ nhớ của vùng nhớ Stack lại nhanh hơn, và được ưu tiên hơn so với vùng nhớ Heap. Do Stack chỉ đơn giản là các kiểu đơn giản nên chỉ có vài thao tác đơn giản như tăng và giảm giá trị, trong khi Heap gặp các vấn đề liên quan tới việc phân bổ trên máy tính.

int a = 1;           //Tạo biến toàn cục
int main() 
{
    int a = 2;       //Tạo biến cục bộ
    cout << a;       //Xuất giá trị a = 2 (của biến cục bộ)
    return 0;
}

Lưu ý: Việc phân bổ của Heap có liên quan tới vấn đề đa luồng, tức nhiều chương trình cùng sử dụng một vùng nhớ, vì thế cần phải nắm rõ mình đang sử dụng vùng nhớ của ai. Tránh việc sử dụng trên vùng nhớ không được cấp phép.

int main() 
{
    int *a;      //Khởi tạo biến a, trỏ tới vị trí không xác định
    (*a)++;      //Tăng giá trị biến a lên có thể sinh ra lỗi
    return;
}

Tóm gọn

Việc sử dụng vùng nhớ Heap hay Stack là do vấn đề của người lập trình. Một chương trình tốt có thể quản lý và sử dụng được khối lượng dữ liệu lớn một cách hợp lý.

Đây là một số tips của mình khi sử dụng 2 vùng nhớ này:

  • Vùng nhớ Stack sử dụng khi biết rõ lượng dữ liệu cần thiết và không quá lớn.
  • Vùng nhớ Heap sử dụng khi không xác định được lượng dữ liệu, hoặc khi biết rõ cách quản lý dữ liệu giữa nhiều chương trình hoặc luồng khác nhau.

Mong là qua bài này, các bạn có thể nắm rõ việc phân bổ và sử dụng vùng nhớ trên các chương trình.

Related Posts
Bộ nhớ Stack và Heap trong Java

Trong Java, quản lý bộ nhớ là một quá trình quan trọng. Nó được quản lý bởi Java một cách tự động. JVM chia Read more

Kiểu dữ liệu Tham chiếu(Reference)

Tổng quan Java cung cấp hai loại dữ liệu kiểu Nguyên thủy và kiểu dữ liệu Tham chiếu . Các kiểu dữ liệu Nguyên thủy được Read more

So sánh ngôn ngữ C++ và Java

Có nhiều điểm khác biệt và tương đồng giữa ngôn ngữ lập trình C++ và Java . Dưới đây là danh sách những điểm khác Read more

Hiển thị trạng thái Heap Memory trong Eclipse

Để hiển thị trạng thái bộ nhớ Heap được sử dụng bởi Eclipse ta làm như sau: Trên thanh Menu Read more

Hãy bình luận đầu tiên

Để lại một phản hồi