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
 
  
 