2009-07-09 43 views
16

Tôi gặp vấn đề trong đó một mảng 3 chiều phân bổ một lượng lớn bộ nhớ và đôi khi chương trình cần thay thế chúng bằng những cái lớn hơn và nhỏ hơn và ném ra một OutOfMemoryException.Buộc thu gom rác của mảng, C#

Ví dụ: có 5 mảng được phân bổ 96MB (200x200x200, 12 byte dữ liệu trong mỗi mục nhập) và chương trình cần thay thế chúng bằng 210x210x210 (111MB). Nó thực hiện theo cách tương tự như sau:

array1 = new Vector3[210,210,210]; 

Trường hợp array1-array5 là các trường tương tự được sử dụng trước đó. Điều này sẽ thiết lập các mảng cũ như các ứng cử viên cho việc thu gom rác nhưng dường như GC không hành động đủ nhanh và để lại các mảng cũ được phân bổ trước khi cấp phát các mảng mới - điều này gây ra OOM - trong khi nếu chúng được giải phóng trước khi phân bổ mới thì không gian đủ.

Những gì tôi đang tìm kiếm là một cách để làm điều gì đó như thế này:

GC.Collect(array1) // this would set the reference to null and free the memory 
array1 = new Vector3[210,210,210]; 

Tôi không chắc chắn nếu một collecion rác đầy đủ sẽ là một ý tưởng tốt kể từ khi mã mà có thể (trong một số trường hợp) cần được thực hiện khá thường xuyên.

Có cách nào phù hợp để thực hiện việc này không?

+2

Dường như thật kỳ lạ là điều này sẽ gây ra ngoại lệ OOM. Bạn có một số mã mẫu thể hiện sự cố không? – aquinas

+0

Đây là một phần của một chương trình hợp lý (vẫn chưa được phát hành) và không may, tôi chỉ có thể đăng các đoạn mã nhỏ của mã thực tế, không có gì đủ lớn để có thể kết hợp được. –

+0

Vấn đề của bạn không phải là quản lý bộ nhớ, nhưng bạn đang sử dụng các mảng quá lớn. Tại sao bạn nghĩ rằng bạn cần những mảng lớn ở nơi đầu tiên? Bạn đang sử dụng chúng để làm gì? – Guffa

Trả lời

14

Đây không phải là một câu trả lời chính xác cho câu hỏi ban đầu, "làm thế nào để buộc GC', tuy nhiên, tôi nghĩ rằng nó sẽ giúp bạn kiểm tra lại vấn đề của bạn.

Sau khi nhìn thấy bình luận của bạn,

  • Đặt GC.Sưu tầm(); dường như giúp đỡ, nên nó vẫn không giải quyết được vấn đề hoàn toàn - vì lý do nào đó chương trình vẫn bị treo khi khoảng 1.3GB được cấp phát (tôi đang sử dụng System.GC.GetTotalMemory (false); để tìm số tiền thực được phân bổ).

Tôi nghi ngờ bạn có thể có phân mảnh bộ nhớ. Nếu đối tượng là lớn (85000 byte theo .net 2.0 CLR nếu tôi nhớ chính xác, tôi không biết liệu nó đã được thay đổi hay không), đối tượng sẽ được phân bổ trong một đống đặc biệt, Large Object Heap (LOH). GC lấy lại bộ nhớ đang được sử dụng bởi các đối tượng không thể truy cập trong LOH, tuy nhiên, nó không thực hiện nén chặt, trong LOH như với các vùng khác (gen0, gen1 và gen2), do hiệu suất. Nếu bạn thường xuyên phân bổ và deallocate các đối tượng lớn, nó sẽ làm cho LOH phân mảnh và mặc dù bạn có nhiều bộ nhớ miễn phí trong tổng số hơn những gì bạn cần, bạn có thể không có một không gian bộ nhớ tiếp giáp nữa, do đó, sẽ nhận được ngoại lệ OutOfMemory .

Tôi có thể nghĩ hai cách giải quyết tại thời điểm này.

  1. Di chuyển đến 64-bit máy/hệ điều hành và tận dụng lợi thế của nó :) (dễ nhất, nhưng có thể khó khăn nhất cũng tùy thuộc vào chế về nguồn lực của bạn)
  2. Nếu bạn không thể làm # 1, sau đó cố gắng phân bổ một mâm cặp lớn của bộ nhớ đầu tiên và sử dụng chúng (nó có thể yêu cầu để viết một số lớp helper để thao tác một mảng nhỏ hơn, mà trong thực tế, nằm trong một mảng lớn hơn) để tránh sự phân mảnh. Điều này có thể giúp một chút, tuy nhiên, nó có thể không hoàn toàn giải quyết vấn đề và bạn có thể phải đối phó với sự phức tạp.
+2

Hoặc chuyển sang sử dụng mảng răng cưa - Vector3 [210] [210] [210], sẽ phân chia các phân bổ và không sử dụng một khối lớn bộ nhớ lớn – thecoop

+0

Dường như vấn đề thực sự là do vấn đề phân mảnh này. Rất tiếc, tôi không thể chuyển chương trình sang 64 bit vì XNA chỉ hỗ trợ 32 bit. Phân bổ một mảng lớn khi khởi động có lẽ sẽ không đáng để nhớ thêm về các trường hợp nhỏ (dưới 100x100x100) và công việc phụ để làm cho nó hoạt động tốt. Tôi đã làm toán và thậm chí nếu tôi có thể sử dụng 2GB đầy đủ, cải thiện về kích thước khối lượng tối đa sẽ không đủ lớn (từ khoảng 200^3 đến 300^3) để đi qua những rắc rối của việc tìm kiếm/thực hiện một hack để làm cho nó hoạt động, ít nhất là không phải bây giờ. Nhờ tất cả những ai đã cố giúp đỡ! –

7

Buộc thu gom rác không phải lúc nào cũng là ý tưởng hay (nó có thể thực sự thúc đẩy tuổi thọ của các vật thể trong một số trường hợp). Nếu bạn phải, bạn sẽ sử dụng:

array1 = null; 
GC.Collect(); 
array1 = new Vector3[210,210,210]; 
+2

Không, bạn không nên gọi GC.Collect cả. Giải pháp tốt nhất là chỉ cần loại bỏ các tài liệu tham khảo trước khi mảng mới được phân bổ và để cho các nhà sưu tập rác làm việc đó mà không can thiệp. – Guffa

+5

Nếu thời gian chạy không thể tìm đủ bộ nhớ cho phân bổ, nó sẽ kích hoạt một bộ sưu tập, vì vậy không cần phải gọi hàm GC.Collect. –

+3

John Doe đã hỏi về * buộc * thu gom rác thải sau khi tất cả. – icelava

1

Chúng có thể không được thu thập vì chúng đang được tham chiếu đến nơi bạn không mong đợi.

Để kiểm tra, hãy thử thay đổi tham chiếu của bạn thành WeakReferences thay vào đó và xem điều đó có giải quyết được sự cố OOM của bạn hay không. Nếu nó không thì bạn đang tham khảo chúng ở một nơi khác.

+0

Chúng chỉ được sử dụng trong mã tạo ra chúng và trong mã sử dụng dữ liệu (XNA VertexBuffer, được sử dụng để hiển thị khối lượng) vì vậy tôi không nghĩ bất cứ điều gì khác tham chiếu đến chúng. Tôi chưa bao giờ sử dụng WeakReferences trước đây nhưng từ bài viết đó tôi nhận được có cơ hội nhận được deallocated trong phần còn lại của việc thực hiện - và điều đó sẽ tạo ra một vấn đề nghiêm trọng cho phần còn lại của mã. –

2

Nếu tôi đã phải suy đoán bạn vấn đề là không thực sự mà bạn đang đi từ Vector3 [200.200.200] để một Vector3 [210.210.210] nhưng mà khả năng bạn có bước trước tương tự trước cái này nhất:

 
i.e. 
    // first you have 
    Vector3[10,10,10]; 
    // then 
    Vector3[20,20,20]; 
    // then maybe 
    Vector3[30,30,30]; 
    // .. and so on .. 
    // ... 
    // then 
    Vector3[200,200,200]; 
    // and eventually you try 
    Vector3[210,210,210] // and you get an OutOfMemoryException.. 

Nếu đó là sự thật, tôi sẽ đề nghị một chiến lược phân bổ tốt hơn. Thử phân bổ quá mức - có thể tăng gấp đôi kích thước mỗi lần trái ngược với việc luôn chỉ phân bổ không gian mà bạn cần. Đặc biệt nếu các mảng đang từng sử dụng bởi các đối tượng mà cần phải ghim bộ đệm (tức là nếu điều đó có quan hệ với mã gốc)

Vì vậy, thay vì ở trên, có một cái gì đó như thế này:

// first start with an arbitrary size 
Vector3[64,64,64]; 
// then double that 
Vector3[128,128,128]; 
// and then.. so in thee steps you go to where otherwise 
// it would have taken you 20.. 
Vector3[256,256,256]; 
+0

Lưu ý rằng một số lớp học được xây dựng trong bộ sưu tập sẽ làm điều này cho bạn. – Brian

+0

Tôi đồng ý, việc liên tục phân bổ lại các mảng như vậy chỉ là việc xin lỗi OOM. –

+0

Trên thực tế, "thay đổi kích thước" của các mảng được kích hoạt bởi đầu vào của người dùng và không thể dự đoán được (tôi sẽ tự tăng nó theo các bước để kiểm tra). Phân bổ tổng thể không phải là một tùy chọn, điều đó thực tế sẽ làm cho nó bị lỗi trước đó, bằng cách sử dụng tổng kích thước tăng theo cách khối. –

0

Một OutOfMemory ngoại lệ trong nội bộ kích hoạt chu kỳ GC tự động một lần và cố gắng phân bổ lại trước khi thực sự ném ngoại lệ vào mã của bạn. Cách duy nhất bạn có thể có ngoại lệ OutOfMemory là nếu bạn đang giữ tham chiếu đến quá nhiều bộ nhớ. Xóa các tài liệu tham khảo ngay sau khi bạn có thể bằng cách gán cho chúng null.

4

Đây không phải là phân đoạn heap đối tượng lớn không? Đối tượng> 85.000 byte được cấp phát trên heap đối tượng lớn. GC giải phóng không gian trong heap này nhưng không bao giờ nén các đối tượng còn lại. Điều này có thể dẫn đến bộ nhớ tiếp giáp không đủ để phân bổ thành công một đối tượng lớn.

Alan.

0

Một phần của sự cố có thể là bạn đang phân bổ mảng đa chiều, được biểu diễn dưới dạng một khối bộ nhớ liền kề duy nhất trên khối đối tượng lớn (xem chi tiết here). Điều này có thể chặn các phân bổ khác vì không có một khối tiếp giáp miễn phí để sử dụng, ngay cả khi vẫn còn một số không gian trống ở đâu đó, do đó là OOM.

Hãy thử phân bổ nó như là một mảng lởm chởm - Vector3 [210] [210] [210] - mà lây lan các mảng xung quanh bộ nhớ hơn là một khối duy nhất, và thấy rằng nếu cải thiện các vấn đề

1

tôi hiểu những gì bạn đang cố gắng để làm và đẩy cho thu gom rác ngay lập tức có lẽ không phải là cách tiếp cận đúng (vì GC là tinh tế theo cách của nó và nhanh chóng tức giận).

Điều đó nói rằng, nếu bạn muốn chức năng đó, tại sao bạn không tạo nó?

public static void Collect(ref object o) 
{ 
    o = null; 
    GC.Collect(); 
} 
+0

Điều đó không làm gì cả. Bạn đang tạo một bản sao của tham chiếu đến đối tượng o point thành null, mà không làm gì với đối tượng ban đầu o. –

+0

Bạn hiểu rằng nếu tôi tác dụng phụ một biến ref, nó cũng thay đổi bản sao của người gọi, phải không? – plinth

+0

Có một +1 trên tôi, mã này thực sự sẽ thiết lập biến ban đầu để trỏ đến null vì nó đang sửa đổi con trỏ của biến ban đầu bằng cách tham chiếu. –

9

Dường như bạn đã gặp phải vấn đề phân mảnh LOH (Lớn đối tượng đống).

Large Object Heap

CLR Inside Out Large Object Heap Uncovered

Bạn có thể kiểm tra để xem nếu bạn đang gặp vấn đề Loh phân mảnh bằng SOS

Kiểm tra question này cho một ví dụ về cách sử dụng SOS để kiểm tra việc Loh.

0

John, Tạo đối tượng> 85000 byte sẽ làm cho đối tượng kết thúc trong vùng đối tượng lớn. Heap đối tượng lớn không bao giờ được nén lại, thay vào đó, không gian trống được sử dụng lại. Điều này có nghĩa rằng nếu bạn đang phân bổ mảng lớn hơn mỗi lần, bạn có thể kết thúc trong các tình huống mà LOH bị phân mảnh, do đó là OOM.

bạn có thể xác minh đây là trường hợp bằng cách phá vỡ trình gỡ lỗi tại điểm của OOM và nhận kết xuất, gửi kết xuất này tới MS thông qua lỗi kết nối (http://connect.microsoft.com) sẽ là một khởi đầu tuyệt vời.

Điều tôi có thể đảm bảo với bạn là GC sẽ làm điều đúng để đáp ứng yêu cầu phân bổ của bạn, điều này bao gồm khởi động GC để làm sạch thùng rác cũ để đáp ứng các yêu cầu phân bổ mới.

Tôi không biết chính sách chia sẻ bộ nhớ trên Stackoverflow là gì, nhưng tôi rất sẵn lòng xem xét vấn đề của bạn nhiều hơn.