0

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ữ trong heap. Nếu quên free() 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()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().

image.png

Khi hàm List_Add được thực thi thì dữ liệu trong vùng nhớ AB đượ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ớ AB 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ớ AB, 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ụng malloc() 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ớ AB thay vì copy.

III. Tham khảo


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí