03. Vấn đề Memory Leak và OutOfMemory trong C#
Bạn đã bao giờ nghe về Memory Leak và OutOfMemory trong C#, giờ chúng ta sẽ tìm hiểu về hai khái niệm này nhé!
Memory Leak
- Vấn đề Memory Leak xảy ra khi một ứng dụng liên tục cấp phát bộ nhớ nhưng lại bỏ qua việc giải phóng nó ngày sau khi sử dụng xong, kết quả là ứng dụng dần dần sử dụng nhiều bộ nhớ hơn theo thời gian, dẫn đến ứng dụng có thể bị crash hoặc ngừng hoạt động. Nhưng trong C# có trình dọn rác GC tự động thu gom những bộ nhớ không còn được sử dụng nữa, vậy tại sao điều này có thể xảy ra? Một số nguyên nhân phổ biến như sau:
- Khi ứng dụng có quá nhiều tham chiếu đến những đối tượng thời điểm trước được cấp phát bộ nhớ để sử dụng nhưng đã hoàn thành xong tác vụ của nó nhưng chưa được giải phóng. Những đối tượng khác tham chiếu đến đối tượng này sẽ ngăn GC lấy lại bộ nhớ đã sử dụng dù đối tượng đó đã hoàn thành nhiệm vụ => bộ nhớ heap liên tục tăng. Ví dụ như đăng ký một event nhưng quên hủy event đó...
- Khi cấp phát bộ nhớ với những tính năng không được quản lý bởi GC và quên giải phóng chúng. Trong C# nhiều lớp để cấp phát bộ nhớ không được quản lý, đó là những thao tác liên quan đến đồ họa, network và những thao tác với file, data stream... thường sẽ được implement interface IDispose nhiệm vụ và sử dụng từ khóa using để giải phóng bộ nhớ, nếu không giải phóng bộ nhớ nó có thể được cấp phát vô thời hạn.
- Sử dụng static object: khi sử dụng lớp hoặc biến static nó sẽ tồn tại xuyên suốt vòng đời của ứng dụng. GC sẽ không thu thập đối tượng static và những tham chiếu của đối tượng đó, vì vậy luôn phải cẩn trọng khi sử dụng static object, không sử dụng static nếu không thực sự cần thiết!
- Query hoặc xử lý Thread trong một thời gian quá dài: việc xử lý những tác vụ quá lâu trong thread do những điều kiện quá nhiều hoặc những vấn đề vòng lặp vô hạn, object sẽ được giữ trong một thời gian dài và GC sẽ không thu hồi được bộ nhớ => cẩn trọng với những vòng lặp và hay đệ quy hàm.
- Mem Caching: Cache là một biện pháp tuyệt vời để tối ưu việc query hay hiệu năng khi lấy dữ liệu, nhưng nếu lưu quá nhiều dữ liệu vào mem cache có thể dẫn đến hết bộ nhớ memory. Vì vậy chỉ nên lưu vào memcache những đối tượng sử dụng thường xuyên với tần suất quá nhiều lần để cải thiện việc truy xuất. Để phát triển ứng dụng một cách hiệu quả và tối ưu cần nắm được khái niệm về Memory Leak là gì và nó xảy ra như thế nào. Vì vậy trong quá trình phát triển phần mềm cần để ý những vấn đề liên quan, và sử dụng những công cụ để debug được memory leak như Diagnostic Tool... trong C# nói riêng hay các ngôn ngữ lập trình khác nói chung.
Out Of Memory
- Out Of Memory Exception xảy ra khi chương trình không đủ memory để thực hiện một tác vụ nào đó. Cụ thể là khi CLR (Common Language Runtime) không có khả năng cấp phát đủ bộ nhớ cho một số hoạt động nhất định của chương trình.
- Có một số nguyên nhân chính xảy ra vấn đề này:
- Cố gắng mở rộng một đối tượng StringBuilder vượt quá độ dài được xác định bởi thuộc tính StringBuilder.MaxCapacity của nó. Ví dụ:
using System.Text;
StringBuilder stringBuilder = new StringBuilder(17, 17);
stringBuilder.Append("Welcome to the ");
try
{
stringBuilder.Insert(0, "world of C# programming", 1);
Console.WriteLine(stringBuilder.ToString());
Console.ReadLine();
}
catch (OutOfMemoryException exception)
{
Console.WriteLine(exception.Message);
Console.ReadLine();
}
Chúng ta sẽ nhận được ngoại lệ: System.OutOfMemoryException: Insufficient memory to continue the execution of the program, vì khi gọi phương thức Insert yêu cầu cấp phát bộ nhớ và đồng thời CLR không thể cung cấp đủ bộ nhớ liên tục để cấp phát thì chúng ta sẽ nhận được OutOfMemoryException.
- Chạy ứng dụng trên hệ thống 32 bit, vì tiến trình 32 bit chỉ cho cấp phát tối đa 2 GB bộ nhớ ảo dẫn đến CLR khó khăn trong việc cấp phát bộ nhớ.
- Ứng dụng không giải phóng tài nguyên không được quản lý bởi GC dẫn đến memory leak một thời gian quá lâu có thể dẫn đến Out Of Memory.
- Làm việc với dữ liệu rất lớn như mảng, tập hợp, datasets... đòi hỏi một lượng bộ nhớ khổng lồ và không gian liền kề đủ cho nó nếu không có thể dẫn đến lỗi.
- Khi làm việc với string, mà lớp string là immutable, mỗi lần tạo một string sẽ tạo một bộ nhớ mới trong heap, nếu làm việc với chuỗi string quá lớn và thực hiện nhiều thao tác với nó cũng dẫn đến không đủ bộ nhớ để thực hiện.
- Pin một số đối tượng lớn trong memory: việc pin một số lượng lớn đối tượng vào bộ nhớ trong thời gian dài có thể gây khó khăn cho trình thu gom rác trong việc phân bổ bộ nhớ, ví dụ như sử dụng fixed statment để pin bộ nhớ, vì vậy cần đánh giá kỹ lưỡng xem đối tượng đó có thực sự cần pin hay không?
Kết luận
Memory Leak và Out Of Memory là hai vấn đề thường gặp trong các ngôn ngữ lập trình, việc hỏi rõ các khái niệm và công cụ debug sâu sẽ giúp ae hiểu rõ được những lỗi tiềm ẩn và phòng ngừa chúng cùng với đó nâng cao hiệu năng của ứng dụng. Cảm ơn ae đã đọc bài viết này!
All rights reserved