Boxing trong unity
Minh Khoa
Author
🎯 1. Boxing là gì? (định nghĩa chuẩn)
Boxing = hành động chuyển một giá trị kiểu value type (int, float, bool, struct…) thành một object (kiểu reference type).
Tức là C# đóng value vào một “hộp object” trên heap.
Ví dụ:
int x = 5;
object obj = x; // ← boxing xảy ra
x từ value type (lưu trên stack) → copy vào một object (lưu trên heap).
🎯 2. Unboxing là gì?
Lấy value từ object ra:
object obj = 10;
int y = (int)obj; // ← unboxing
🎯 3. Tại sao BOXING gây hại trong Unity?
❌ (1) Boxing tạo ra memory allocation → gây GC
Khi boxing, C# tạo ra một object mới trên heap.
→ Alloc
→ Sau đó GC phải dọn → gây spike, drop FPS.
Trong game, đặc biệt là update loop → rất nguy hiểm.
❌ (2) Xảy ra ngầm → khó phát hiện
Ví dụ:
int x = 10;
Debug.Log(x);
Có boxing vì Log nhận parameter là object.
❌ (3) Boxing xảy ra khi dùng interface
Nếu struct implement interface → gọi interface method → BOX.
public interface IAttack { void Do(); }
public struct Sword : IAttack
{
public void Do() { }
}
IAttack atk = new Sword(); // boxing!
❌ (4) Boxing khi dùng generic không constraint tốt
Ví dụ:
void Print<T>(T value)
{
Debug.Log(value); // boxing nếu value là struct
}
❌ (5) LINQ rất hay gây boxing
LINQ to Object tạo delegate, iterator → allocation + boxing.
🎯 4. Một số ví dụ boxing phổ biến trong Unity
❌ Ví dụ 1: Debug.Log
int score = 10;
Debug.Log(score); // boxing
Cách fix:
Debug.Log($"{score}"); // không boxing (string format)
❌ Ví dụ 2: Struct trong List<object>
List<object> list = new();
list.Add(5); // boxing
❌ Ví dụ 3: Sử dụng interface với struct
IMovement mover = new MovementStruct(); // boxing
❌ Ví dụ 4: foreach với struct enumeration
foreach (var item in myStructList) { ... }
// nếu enumerator là struct → có thể boxing tùy implement
❌ Ví dụ 5: Event với struct argument
Action<MyStruct> onChange;
onChange(myStruct); // có thể boxing
🎯 5. Làm sao để tránh boxing?
✔ 1. Tránh dùng object để chứa value type
Sai:
object obj = 5;
Đúng:
int x = 5;
✔ 2. Tránh interface nếu struct implement interface
Dùng generic thay thế.
*Generic (hay lập trình tổng quát) không phải là một kiểu dữ liệu cụ thể (như int, string), mà là khái niệm tham số hóa kiểu dữ liệu (parameterized types). Nó cho phép định nghĩa các lớp (class), giao diện (interface) và phương thức (method) với kiểu dữ liệu chưa xác định, giúp mã nguồn linh hoạt, tái sử dụng cao và kiểm soát lỗi chặt chẽ tại thời điểm biên dịch
Sai:
IAttack atk = new Sword();
Đúng:
void Perform<T>(T atk) where T : IAttack
{
atk.Do();
}
✔ 3. Debug.Log → ép string để tránh boxing
Debug.Log(score.ToString());
✔ 4. Tối ưu system dùng struct (job system, ECS)
ECS / Burst hoàn toàn không boxing.
✔ 5. Tránh boxing trong Dictionary key
Nếu key là struct → có thể boxing khi GetHashCode() override kém.
🎯 6. Làm sao biết trong code có boxing?
Unity Editor:
- Bật Profiler → GC Alloc
- Hoặc bật Deep Profile
Nếu mỗi frame có:
System.Object
System.Int32
System.ValueType
→ đó là BOXING.
🎯 7. Tóm tắt siêu dễ nhớ
BOXING = struct → object → tạo rác → gây GC → drop FPS
Unity sợ nhất GC → phải tránh boxing trong:
- Update()
- LateUpdate()
- FixedUpdate()
- UI event spam
- DOTS / Jobs
- Network serialization