May mắn thay, như bạn đã chỉ ra, các bản dựng COMPACT Mono sử dụng một thế hệ GC (trái ngược hoàn toàn với các Microsoft, như WinMo / WinPhone / XBox, người chỉ duy trì một danh sách phẳng).
Nếu trò chơi của bạn đơn giản thì GC sẽ xử lý tốt, nhưng đây là một số gợi ý bạn có thể muốn xem xét.
Tối ưu hóa sớm
Trước tiên hãy chắc chắn rằng đây thực sự là một vấn đề cho bạn trước khi cố gắng khắc phục nó.
Pooling Các loại tài liệu tham khảo đắt tiền
Bạn nên gộp các kiểu tham chiếu mà bạn tạo thường xuyên hoặc có cấu trúc sâu. Một ví dụ về mỗi cái sẽ là:
- Tạo thường xuyên: Một
Bullet
đối tượng trong một trò chơi địa ngục .
- Cấu trúc sâu: Cây quyết định cho việc triển khai AI.
Bạn nên sử dụng Stack
như một nhóm của bạn (không giống như hầu hết các triển khai sử dụng a Queue
). Lý do cho điều này là vì với mộtStack
nếu bạn trả lại một vật thể cho nhóm và một cái gì đó ngay lập tức lấy nó; nó sẽ có cơ hội cao hơn trong một trang hoạt động - hoặc thậm chí trong bộ đệm CPU nếu bạn may mắn. Nó chỉ nhanh hơn một chút thôi. Hơn nữa, luôn luôn giới hạn kích thước nhóm của bạn (chỉ cần bỏ qua 'checkins' nếu giới hạn của bạn đã bị vượt quá).
Tránh tạo danh sách mới để xóa chúng
Đừng tạo một cái mới List
khi bạn thực sự có ý nghĩa với Clear()
nó. Bạn có thể sử dụng lại mảng phụ trợ và lưu một lượng phân bổ mảng và bản sao. Tương tự như vậy, hãy thử và tạo danh sách với công suất ban đầu có ý nghĩa (hãy nhớ rằng đây không phải là giới hạn - chỉ là công suất bắt đầu) - không cần chính xác, chỉ là ước tính. Điều này nên áp dụng cho cơ bản bất kỳ loại bộ sưu tập nào - ngoại trừ a LinkedList
.
Sử dụng Mảng cấu trúc (hoặc danh sách) khi có thể
Bạn có được ít lợi ích từ việc sử dụng các cấu trúc (hoặc các loại giá trị nói chung) nếu bạn chuyển chúng xung quanh giữa các đối tượng. Ví dụ, trong hầu hết các hệ thống hạt 'tốt', các hạt riêng lẻ được lưu trữ trong một mảng lớn: mảng và chỉ mục được truyền xung quanh thay vì chính hạt đó. Lý do điều này hoạt động rất tốt là vì khi GC cần thu thập mảng, nó có thể bỏ qua toàn bộ nội dung (đó là mảng nguyên thủy - không có gì để làm ở đây). Vì vậy, thay vì nhìn vào 10 000 đối tượng, GC chỉ cần nhìn vào 1 mảng: mức tăng khổng lồ! Một lần nữa, điều này sẽ chỉ làm việc với các loại giá trị .
Sau RoyT. cung cấp một số phản hồi khả thi và mang tính xây dựng Tôi cảm thấy tôi cần mở rộng thêm về điều này. Bạn chỉ nên sử dụng kỹ thuật này khi bạn đang xử lý một lượng lớn thực thể (hàng ngàn đến hàng chục nghìn). Ngoài ra, nó phải là một cấu trúc không được có bất kỳ trường loại tham chiếu nào và phải sống trong một mảng được gõ rõ ràng. Trái với phản hồi của anh ấy, chúng tôi đang đặt nó trong một mảng rất có khả năng là một trường trong một lớp - có nghĩa là nó sẽ tăng lên đống (chúng tôi không cố gắng tránh phân bổ heap - chỉ đơn thuần là tránh công việc của GC). Chúng tôi thực sự quan tâm đến thực tế rằng đó là một đoạn bộ nhớ liền kề với rất nhiều giá trị mà GC có thể chỉ cần nhìn vào trong một O(1)
hoạt động thay vì một O(n)
hoạt động.
Bạn cũng nên phân bổ các mảng này càng gần với khởi động ứng dụng của bạn càng tốt để giảm thiểu khả năng xảy ra sự phân mảnh hoặc làm việc quá mức khi GC cố gắng di chuyển các khối này xung quanh, (và xem xét sử dụng danh sách được liên kết lai thay vì List
loại tích hợp ).
GC.Collect ()
Đây chắc chắn là cách TỐT NHẤT để tự bắn vào chân mình (xem: "Cân nhắc về hiệu suất") với một thế hệ GC. Bạn chỉ nên gọi nó khi bạn đã tạo ra một lượng rác EXTREME - và một trường hợp có thể là vấn đề chỉ sau khi bạn đã tải nội dung cho một cấp độ - và thậm chí sau đó bạn có thể chỉ nên thu thập thế hệ đầu tiên ( GC.Collect(0);
) để hy vọng ngăn chặn việc thúc đẩy các đối tượng đến thế hệ thứ ba.
IDis Dùng một lần và Nulling
Nó đáng giá cho các trường null khi bạn không còn cần một đối tượng (hơn nữa đối với các đối tượng bị ràng buộc). Lý do là trong các chi tiết về cách thức hoạt động của GC: nó chỉ loại bỏ các đối tượng không được root (tức là được tham chiếu) ngay cả khi đối tượng đó đã không được điều khiển vì các đối tượng khác bị xóa trong bộ sưu tập hiện tại ( lưu ý: điều này phụ thuộc vào GC hương vị trong sử dụng - một số thực sự làm sạch chuỗi). Ngoài ra, nếu một đối tượng sống sót trong một bộ sưu tập, nó sẽ ngay lập tức được thăng cấp cho thế hệ tiếp theo - điều này có nghĩa là bất kỳ đối tượng nào nằm xung quanh trong các trường sẽ được thăng cấp trong một bộ sưu tập. Mỗi thế hệ kế tiếp đắt hơn theo cấp số nhân (và xảy ra không thường xuyên).
Lấy ví dụ sau:
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// G1 Collection
MyNestObject (G2) -> MyFurtherNestedObject (G2)
// G2 Collection
MyFurtherNestedObject (G3)
Nếu MyFurtherNestedObject
có chứa một đối tượng nhiều megabyte, bạn có thể được đảm bảo rằng GC sẽ không nhìn vào nó trong một thời gian khá dài - bởi vì bạn đã vô tình quảng cáo nó lên G3. Tương phản với ví dụ này:
MyObject (G1) -> MyNestedObject (G1) -> MyFurtherNestedObject (G1)
// Dispose
MyObject (G1)
MyNestedObject (G1)
MyFurtherNestedObject (G1)
// G1 Collection
Mẫu Disposeer giúp bạn thiết lập một cách có thể dự đoán để yêu cầu các đối tượng xóa các trường riêng của chúng. Ví dụ:
public class MyClass : IDisposable
{
private MyNestedType _nested;
// A finalizer is only needed IF YOU CONTROL UNMANAGED RESOURCES
// ~MyClass() { }
public void Dispose()
{
_nested = null;
}
}