Làm thế nào để trò chơi C ++ xử lý lỗi cấp phát bộ nhớ?


23

Tôi biết một số trò chơi được viết bằng C ++ nhưng không sử dụng ngoại lệ. Do việc xử lý lỗi cấp phát bộ nhớ trong C ++ thường được xây dựng xung quanh std::bad_allocngoại lệ, làm thế nào để các trò chơi này xử lý một lỗi như vậy?

Có phải họ chỉ đơn giản là gặp sự cố, hoặc có một cách khác để xử lý và phục hồi từ lỗi hết bộ nhớ?


1
Lưu ý rằng có một số môi trường / HĐH nhất định nơi phân bổ tuyên bố là thành công, nhưng cố gắng sử dụng bộ nhớ làm hỏng chương trình của bạn (hoặc khác)
PlasmaHH

6
Nếu việc phân bổ thất bại trong trò chơi, Quake 3 sẽ đưa bạn trở lại menu với thông báo lỗi. Tôi tin rằng điều này được thực hiện bởi người phân bổ nhóm của Quake 3, người có thể thả toàn bộ nhóm và quay trở lại menu một cách an toàn nếu hết hồ bơi.
Dietrich Epp

16
Thông thường, bằng cách sụp đổ.
dùng253751

1
Nếu ngoại lệ thực sự bị vô hiệu hóa, chương trình phải gọi std::terminatevà thay vào đó là chương trình . Nhưng sau đó trong cùng một hạn chế, phân bổ có thể trả về một con trỏ null thay vì ném một ngoại lệ, và kết quả đó có thể được kiểm tra và xử lý riêng.
gạch dưới

2
Trong C ++, bạn có thể nói ClassName variableName = new(nothrow) ClassName();( rõ ràng thay thế tên lớp, tên biến, v.v. ) Sau đó, nếu phân bổ thất bại, bạn có thể phát hiện nó bằng cách nói if(!variableName)cho phép xử lý lỗi mà không có khối ngoại lệ thử bắt. Tương tự, nếu bộ nhớ được phân bổ bằng cách sử dụng một chức năng như malloc(), calloc()v.v., thì các lỗi không thể phân bổ có thể được phát hiện bằng cách sử dụng cùng một if(!variableName)phương thức mà không cần thử bắt. Đối với cách các trò chơi xử lý các lỗi này, thì tại thời điểm đó, các nhà phát triển trò chơi phải quyết định xem nó có bị treo hay không.
Spencer D

Câu trả lời:


63

Giống như tất cả các chương trình trung bình: Họ không. *

Đối với hầu hết các ứng dụng, không có mong muốn của khách hàng rằng chúng tiếp tục hoạt động khi hết bộ nhớ. Tất cả các trò chơi thuộc "hầu hết các ứng dụng" này. Dành thời gian và tiền bạc để làm việc trong một trường hợp cạnh mà khách hàng không mong đợi làm việc không có ý nghĩa.

Câu hỏi tương tự như sau:

  • Làm thế nào để bạn cài đặt một trò chơi nếu đĩa cứng đầy?
  • Làm thế nào để bạn vẫn chạy trò chơi ở tốc độ cao / giây trên PC dưới thông số kỹ thuật tối thiểu?

Câu trả lời là như nhau: Đây là những vấn đề của người dùng. Trò chơi không quan tâm.


* Trên thực tế, họ thường bắt ngoại lệ ở mức độ cao và sử dụng bộ nhớ được phân bổ trước khi bắt đầu trò chơi để cố gắng đăng nhập sự kiện trước khi gặp sự cố / chấm dứt. Nhật ký sau đó cho phép hỗ trợ khách hàng lãng phí ít thời gian hơn cho vấn đề.


2
Trên bộ nhớ preallocating để ghi nhật ký, v.v ... trong trường hợp không cấp phát bộ nhớ: stackoverflow.com/questions/7749066/iêu
bwDraco

23

Điển hình là loại kịch bản không bao giờ xảy ra.

Thứ nhất, bộ nhớ ảo trên các hệ điều hành hiện đại có nghĩa là rất khó xảy ra trong hoạt động bình thường; trừ khi bạn có lỗi phân bổ chạy trốn, trò chơi sẽ phân bổ bộ nhớ từ không gian địa chỉ ảo của hệ điều hành và HĐH sẽ được chăm sóc sau khi phân trang vào và ra.

Điều đó rất tốt và tốt, nhưng nó không thực sự áp dụng cho các hệ máy console hoặc HĐH cũ, vì vậy thứ hai, các trò chơi thậm chí không thực hiện nhiều phân bổ động nhỏ bằng cách sử dụng các cấp phát thư viện tiêu chuẩn. Thay vào đó, những gì họ sẽ làm là phân bổ một nhóm bộ nhớ lớn chỉ một lần khi khởi động và rút xuống từ nhóm đó cho bất kỳ phân bổ thời gian chạy nào được yêu cầu (ví dụ: bằng cách sử dụng vị trí C ++ mới hoặc bằng cách viết hệ thống quản lý bộ nhớ của riêng họ trên đỉnh của nó).

Điều đó cũng bảo vệ chống rò rỉ bộ nhớ vì thay vì phải theo dõi và tính toán cho từng phân bổ nhỏ lẻ, một trò chơi chỉ có thể vứt bỏ toàn bộ nhóm để lấy lại bộ nhớ.

Và thứ ba, các trò chơi sẽ luôn đặt một thông số tối thiểu và ngân sách sử dụng bộ nhớ của chúng trong đó. Vì vậy, nếu một trò chơi được chỉ định yêu cầu 1gb RAM, điều đó có nghĩa là miễn là mức sử dụng bộ nhớ của nó không bao giờ vượt quá 1gb, thì không bao giờ phải lo lắng về việc hết RAM.


3
"Thay vào đó, những gì họ sẽ làm là phân bổ một nhóm bộ nhớ lớn chỉ một lần khi khởi động và rút xuống từ nhóm đó cho bất kỳ phân bổ thời gian chạy nào được yêu cầu" - và bạn nghĩ điều gì sẽ xảy ra nếu nhóm đó đầy?
dùng253751

@immibis - xem điểm thứ ba của tôi xin vui lòng.
Maximus Minimus

2
@immibis Ý bạn là ... họ làm gì nếu hồ bơi trống? Không giống như bộ nhớ hệ thống, kích thước và mức độ sử dụng của nhóm hoàn toàn nằm dưới sự kiểm soát của ứng dụng. Vì vậy, pool không thể để trống trừ khi ứng dụng có lỗi.
David Schwartz

@DavidSchwartz Hoặc trừ khi kernel đang sử dụng bộ nhớ quá mức. Linux làm điều này. Ngay cả việc loại bỏ nhóm của bạn cũng không giúp ích gì nếu tính năng nén pagefile được bật thông qua zswap hoặc zram.
Damian Yerrick 8/8/2016

1
Điều này dường như không trả lời câu hỏi thực tế mà thay vào đó nói một điều tương tự như "Con tàu này không thể tưởng tượng được, vậy chúng ta cần một thủ tục khẩn cấp để làm gì?"
Lilienthal

2

Chà, chủ yếu, giống như cách chúng ta đã làm trước khi có ngoại lệ - "kiểm tra cách tiếp cận giá trị trả lại" cũ. Nếu một công cụ cấp phát không sử dụng ngoại lệ, nó thường sẽ trả về nullkhi phân bổ không thành công. Một trò chơi được viết tốt sẽ kiểm tra điều đó và xử lý tình huống theo cách an toàn. Đôi khi, điều này có nghĩa là chấm dứt trò chơi (ví dụ std::terminate) hoặc ít nhất là trở lại trạng thái an toàn đã biết cuối cùng (ví dụ: khi bạn phân bổ từ một nhóm, bạn có thể loại bỏ toàn bộ nhóm ngay cả khi ở trạng thái không an toàn).

Nhiều trò chơi sử dụng ngoại lệ cho các trường hợp như thế này ngay cả sau đó. Khi ai đó nói "tránh sử dụng ngoại lệ trong mã trò chơi", điều họ thường nói là "chỉ sử dụng ngoại lệ cho các trường hợp thực sự đặc biệt, không phải cho kiểm soát dòng chảy trần tục". Thiết lập để bắt ngoại lệ bộ nhớ là rẻ - chi phí chủ yếu là do ném, và các biến chứng bạn gặp phải khi xử lý ngoại lệ. Cả hai điều này đều rất quan trọng trong trường hợp hết bộ nhớ điển hình - bạn hầu như luôn muốn chấm dứt bằng mọi cách.

Tâm trí bạn, hầu hết các trò chơi sẽ sụp đổ. Điều này không điên rồ như bạn nghĩ - bạn thường có một số mục tiêu sử dụng bộ nhớ làm mục tiêu và đảm bảo trò chơi của bạn không đạt đến giới hạn đó (ví dụ: bằng cách sử dụng giới hạn số đơn vị trong trò chơi RTS hoặc bằng cách giới hạn số lượng kẻ thù trong một phần của FPS). Ngay cả khi bạn không, bạn thường không hết bộ nhớ trên bất kỳ hệ thống hiện đại hợp lý nào - chẳng hạn như bạn hết dung lượng địa chỉ ảo (trong ứng dụng 32 bit) hoặc ngăn xếp. HĐH đã giả vờ rằng bạn có nhiều bộ nhớ như bạn yêu cầu - đó là một trong những điều trừu tượng mà bộ nhớ ảo cung cấp. Vấn đề là, chỉ vì bạn có 256 MiB RAM vật lý không có nghĩa là ứng dụng của bạn không thể sử dụng 4 GiB bộ nhớ. Một số trò chơi thậm chí có thể không bận tâm quá nhiều rằng tất cả dữ liệu của họ không phải là '


1

Nắm bắt một ngoại lệ Quyết định làm những gì khi và ngoại lệ được nêu ra là hai điều khác nhau.

Bạn cần phải có logic phức tạp để giải phóng bộ nhớ mà chương trình của bạn không cần. Tệ hơn, nếu bất kỳ chương trình hoặc thư viện đang chạy nào khác bị rò rỉ bộ nhớ thì bạn không có quyền kiểm soát nó

Chỉ có điều tốt là bạn có thể làm điều đó để tắt máy một cách duyên dáng và đó là điều mà hầu hết các chương trình sẽ chọn làm. Bạn thậm chí không thể quyết định đợi cho đến khi bộ nhớ khả dụng vì nó sẽ khiến chương trình của bạn không phản hồi.


Nếu các trò chơi được đề cập theo nghĩa đen là "không sử dụng ngoại lệ" như OP đã nói, thì chúng không thể bắt, nâng hoặc xử lý một trò chơi. Nếu ngoại lệ bị vô hiệu hóa và ai đó ném một cái, thay vào đó std::terminate, chương trình phải gọi và đó là nó. Nhưng trong các điều kiện như vậy, phân bổ có thể trả về một con trỏ null thay vì ném một ngoại lệ và điều đó có thể được xử lý riêng.
gạch dưới
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.