Stack vs. Heap: Tối ưu bộ nhớ trong Unity

Tags:
Optimization
Performance
C#
Architecture

C# Code

/*
 * STACK (Ngăn xếp):
 * - Rất nhanh, quản lý theo kiểu LIFO (Vào sau ra trước).
 * - Mỗi luồng (thread) có một Stack riêng.
 * - Lưu trữ "Value Types" (kiểu giá trị): int, float, bool, Vector3, Quaternion, và các 'struct' bạn tự định nghĩa.
 * - Khi một hàm kết thúc, bộ nhớ trên Stack cho các biến của nó sẽ được giải phóng ngay lập tức.
 * - Rất thân thiện với hiệu năng trong Unity.
 */

public struct Point { public int x, y; }

public void StackExample()
{
    // 'a' và 'p1' là value types, được tạo trực tiếp trên Stack.
    int a = 10;
    Point p1 = new Point { x = 5, y = 5 };

    // Khi gán, toàn bộ dữ liệu được sao chép.
    int b = a; 
    Point p2 = p1;

    b = 20; // Không ảnh hưởng đến 'a'.
    p2.x = 30; // Không ảnh hưởng đến 'p1'.
}

/*
 * HEAP (Đống):
 * - Chậm hơn Stack, là vùng nhớ lớn và linh hoạt.
 * - Lưu trữ "Reference Types" (kiểu tham chiếu): tất cả các 'class', string, array.
 * - Khi tạo một đối tượng bằng 'new MyClass()', bộ nhớ được cấp phát trên Heap.
 * - Biến của bạn trên Stack chỉ là một 'con trỏ' trỏ tới địa chỉ của đối tượng trên Heap.
 * - Việc giải phóng bộ nhớ trên Heap được thực hiện bởi Garbage Collector (GC), 
 *   đây là một tiến trình tốn kém có thể gây giật game (GC spike).
 */

public class Enemy { public int health; }

public void HeapExample()
{
    // 'enemy1' là một tham chiếu trên Stack, trỏ đến một đối tượng Enemy trên Heap.
    Enemy enemy1 = new Enemy { health = 100 };

    // 'enemy2' là một tham chiếu khác trên Stack, trỏ đến CÙNG một đối tượng trên Heap.
    Enemy enemy2 = enemy1;

    enemy2.health = 50; // Thay đổi dữ liệu qua 'enemy2'.
    // Vì cả hai cùng trỏ đến một đối tượng, 'enemy1.health' bây giờ cũng là 50.
}

/*
 * KẾT LUẬN CHO UNITY DEV:
 * - Ưu tiên dùng 'struct' cho các kiểu dữ liệu nhỏ, bất biến (immutable) để tránh tạo rác cho GC.
 * - Chỉ dùng 'class' cho các đối tượng phức tạp, có trạng thái, và cần được chia sẻ qua tham chiếu.
 * - Tránh việc 'new' các class trong hàm Update() bằng mọi giá!
 */

Hiểu cách C# và Unity quản lý hai vùng nhớ Stack và Heap là chìa khóa để tối ưu hiệu năng. Việc sử dụng sai `class` (Heap) thay vì `struct` (Stack) cho các dữ liệu nhỏ có thể tạo ra hàng ngàn 'rác' (garbage), khiến Garbage Collector (GC) phải hoạt động và gây giật, lag game.

Bình luận (0)

Bạn cần đăng nhập để có thể bình luận.

Chưa có bình luận nào. Hãy là người đầu tiên!

Bài viết liên quan

Boxing là quá trình chuyển một Value Type (như int, float) lên Heap bằng cách 'gói' nó trong một object. Unboxing là quá trình ngược lại. Trong Unity, việc này xảy ra âm thầm và thường là nguyên nhân gây ra các vấn đề về Garbage Collection và sụt giảm FPS mà không ai ngờ tới.

Optimization
Performance
C#

Expression Trees là một tính năng C# nâng cao cho phép bạn biểu diễn code dưới dạng cây dữ liệu. Trong Unity, nó mở ra khả năng xây dựng các hệ thống phức tạp như trình tạo truy vấn động, hệ thống AI có thể sửa đổi hành vi tại runtime, hoặc các công cụ editor tùy chỉnh mà không cần sinh mã code mới.

Architecture
Algorithm
Optimization
C#

Giới thiệu triết lý Thiết kế Hướng Dữ liệu (Data-Oriented Design) và cách nó được hiện thực hóa qua kiến trúc ECS (Entity Component System) trong DOTS của Unity để đạt được hiệu năng xử lý dữ liệu vượt trội.

Architecture
Optimization
Performance
DOTS
ECS

Hiểu rõ khi nào nên sử dụng `struct` thay cho `class` để giảm áp lực lên bộ nhớ và garbage collector, đặc biệt hữu ích cho các đối tượng dữ liệu nhỏ, bất biến.

Optimization
Performance
Architecture

LINQ rất mạnh mẽ nhưng có thể gây tốn hiệu năng nếu không sử dụng cẩn thận. Snippet này chỉ ra các mẹo và cạm bẫy khi dùng LINQ trong game.

Optimization
Performance
LINQ
Architecture

So sánh các thuật toán sắp xếp như Bubble Sort, Insertion Sort và Quick Sort và khi nào nên sử dụng chúng trong game (ví dụ: sắp xếp bảng xếp hạng).

Algorithm
Architecture
Optimization

Giới thiệu về mẫu thiết kế Dependency Injection (DI) và tại sao nó giúp code của bạn dễ bảo trì, dễ test và giảm sự phụ thuộc cứng (hard dependencies).

Architecture
Design Pattern
Optimization

Hướng dẫn cài đặt và sử dụng Zenject, một framework Dependency Injection phổ biến cho Unity, để quản lý các phụ thuộc trong dự án một cách hiệu quả.

Architecture
Design Pattern
DI
Zenject
Optimization

Một hệ thống Object Pool đơn giản để tái sử dụng các đối tượng thay vì tạo và hủy liên tục, giúp tối ưu hiệu năng đáng kể, đặc biệt khi cần tạo nhiều đối tượng như đạn, kẻ địch.

Design Pattern
Object Pool
Optimization
Performance

Giới thiệu về Clean Architecture, một triết lý thiết kế phần mềm giúp tách biệt các mối quan tâm thành các lớp riêng biệt (Entities, Use Cases, Adapters, Frameworks), tạo ra một hệ thống độc lập, dễ kiểm thử và bảo trì.

Architecture
Design Pattern
DI
Optimization