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.
Để lại một phản hồi
Bạn phải đăng nhập để gửi phản hồi.