Memory leak trong C
I. 📝Memory leak là gì ?
- Điều này xảy ra khi ta dùng cấp phát động
malloc()
. Khi cấp phát động thì vùng nhớ đó sẽ được lưu trữ trongheap
. Nếu quênfree()
thì vùng nhớ đó sẽ tồn tại mãi trong khi chương trình chạy (lưu ý vùng nhớ này không được sử dụng nữa). Đó gọi làmemory leak
. (hay rò rĩ bộ nhớ). - Memory leak sẽ làm giảm hiệu suất hệ thống bằng cách làm giảm dung lượng
heap
.
Lý thuyết chỉ đơn giản như thế thôi 😆.
II. Ví dụ
1. Vấn đề 1
Mình sẽ đưa ra một trường hợp xảy ra memory leak mà đã gặp trong dự án cá nhân.
#include <stdio.h>
#include <stdlib.h>
typedef struct Student {
int mssv;
int age;
} Student_t;
typedef struct ListStudent {
int number; // só lương học sinh
int current; // số lượng hộc sinh hiện tại
Student_t* Students; // mảng quản lí các học sinh
} List_t;
List_t* List_Init(int number) {
List_t* newList = (List_t *)malloc(sizeof(List_t));
newList->number = number;
newList->current = 0;
newList->Students = (Student_t *)malloc(sizeof(Student_t) * number);
return newList;
}
Student_t* Student_Init(int mssv, int age) {
Student_t* newStudent = (Student_t *)malloc(sizeof(Student_t));
newStudent->mssv = mssv;
newStudent->age = age;
return newStudent;
}
void List_Add(List_t* list, Student_t* student) {
list->Students[list->current] = *student;
list->current++;
}
int main()
{
List_t *list_p = List_Init(2);
Student_t* A = Student_Init(1, 20);
Student_t* B = Student_Init(2, 20);
List_Add(list_p, A);
List_Add(list_p, B);
printf("So luong sinh vien: %d\n", list_p->current);
}
Output
So luong sinh vien: 2
Nếu bạn xem ví dụ này, thì hãy suy nghĩ 1 tý thử vấn đề nằm ở đâu nhé.
Nói sơ qua, ở đây mk cấp phát 2 vùng nhớ bằng 2 hàm là List_Init()
và Student_Init()
.
- Trong
List_Init()
lại cấp phát tiếp 1 mảng để lưu trữ các học sinh. - Tổng cộng là có 3 vị trí dùng
malloc
.
Vậy memory leak xảy ra ở đâu ❓
Xảy ra trong hàm Student_Init()
.
Khi hàm List_Add
được thực thi thì dữ liệu trong vùng nhớ A
và B
được copy vào vùng nhớ do Students
(member của list) quản lí. Lúc này dữ liệu đã có trong mảng Students
nhưng 2 vùng nhớ A
và B
vẫn còn tồn tại mặc dù đã hoàn thành nhiệm. Điều này sẽ gây ra 2 vùng nhớ bị chiếm dụng nhưng không được sử dụng.
Để biết vì sao List_Add
copy mà không phải trỏ đến vùng nhớ A
và B
, hãy đọc bài viết: Cấp phát bộ nhớ trong struct
Hướng giải quyết
- Phương án 1: Sử dụng
free()
để có thể giải phóng hai vùng nhớ này sau khi thực hiện xong quá trình thêm vào mảng. - Phương án 2: Tạo đối tượng cố định trong hàm
Student_Init()
không sử dụngmalloc()
thì không cần lo về memory leak.
Student_t Student_Init(int mssv, int age) {
Student_t newStudent;
newStudent.mssv = mssv;
newStudent.age = age;
return newStudent;
}
void List_Add(List_t* list, Student_t student) {
list->Students[list->current] = student;
list->current++;
}
Khi tạo một đối tượng trong hàm Student_Init()
thì nó được xem như là một biến local được lưu trữ trên stack. Sau khi thực hiện xong nhiệm vụ, hệ điều hành sẽ tự giải phóng vùng nhớ nằm trên stack.
- Phương án 3: Dùng mảng các con trỏ
typedef struct ListStudent {
int number; // số lương học sinh
int current; // số lượng hộc sinh hiện tại
Student_t** Students; // mảng các con trỏ quản lí học sinh
} List_t;
List_t* List_Init(int number) {
List_t* newList = (List_t *)malloc(sizeof(List_t));
newList->number = number;
newList->current = 0;
newList->Students = (Student_t **)malloc(sizeof(Student_t*) * number);
return newList;
}
void List_Add(List_t* list, Student_t* student) {
list->Students[list->current] = student;
list->current++;
}
Mỗi con trỏ trong mảng sẽ trỏ tới vùng nhớ A
và B
thay vì copy.
III. Tham khảo
All rights reserved