Có gì sai khi sử dụng GC.Collect ()?


103

Mặc dù tôi hiểu những tác động nghiêm trọng của việc chơi với chức năng này (hoặc ít nhất đó là những gì tôi nghĩ), tôi không hiểu tại sao nó lại trở thành một trong những thứ mà các lập trình viên đáng kính sẽ không bao giờ sử dụng, ngay cả những người thậm chí không biết nó dùng để làm gì.

Giả sử tôi đang phát triển một ứng dụng mà việc sử dụng bộ nhớ rất khác nhau tùy thuộc vào những gì người dùng đang làm. Vòng đời ứng dụng có thể được chia thành hai giai đoạn chính: chỉnh sửa và xử lý thời gian thực. Trong giai đoạn chỉnh sửa, giả sử rằng hàng tỷ hoặc thậm chí hàng nghìn tỷ đối tượng được tạo ra; một số trong số chúng nhỏ và một số trong số chúng không, một số có thể có chất hoàn thiện và một số có thể không, và giả sử thời gian tồn tại của chúng thay đổi từ vài mili giây đến nhiều giờ. Tiếp theo, người dùng quyết định chuyển sang giai đoạn thời gian thực. Tại thời điểm này, giả sử rằng hiệu suất đóng một vai trò cơ bản và sự thay đổi nhỏ nhất trong quy trình của chương trình có thể mang lại hậu quả thảm khốc. Việc tạo đối tượng sau đó được giảm xuống mức tối thiểu có thể bằng cách sử dụng các nhóm đối tượng và những thứ đó nhưng sau đó, GC kêu bất ngờ và ném tất cả đi, và ai đó chết.

Câu hỏi: Trong trường hợp này, sẽ không khôn ngoan nếu gọi GC.Collect () trước khi bước vào giai đoạn thứ hai?

Rốt cuộc, hai giai đoạn này không bao giờ trùng lặp về thời gian với nhau và tất cả các thống kê và tối ưu hóa mà GC có thể thu thập được sẽ rất ít được sử dụng ở đây ...

Lưu ý: Như một số bạn đã chỉ ra, .NET có thể không phải là nền tảng tốt nhất cho một ứng dụng như thế này, nhưng điều đó nằm ngoài phạm vi của câu hỏi này. Mục đích là để làm rõ liệu một lệnh gọi GC.Collect () có thể cải thiện hành vi / hiệu suất tổng thể của ứng dụng hay không. Tất cả chúng tôi đều đồng ý rằng những trường hợp mà bạn sẽ làm điều như vậy là cực kỳ hiếm nhưng sau đó, GC cố gắng đoán và thực hiện nó hoàn toàn tốt trong hầu hết thời gian, nhưng nó vẫn chỉ là phỏng đoán.

Cảm ơn.


24
"sự thay đổi nhỏ nhất trong quy trình của chương trình có thể mang lại hậu quả thảm khốc ... ai đó có thể chết" - bạn có chắc C # .NET đủ xác định cho mục đích của bạn không?
Steve Jessop 23/09/08

4
Không phải Windows hay .NET đều là nền tảng thời gian thực và do đó bạn không thể đảm bảo các chỉ số hiệu suất, ít nhất là không đủ đến mức nguy hiểm đến tính mạng con người. Tôi đồng ý với một điều rằng bạn đang phóng đại hoặc bất cẩn.
Sergio Acosta

3
LOL tại "một trong những thứ mà các lập trình viên đáng kính sẽ không bao giờ sử dụng, ngay cả những người thậm chí không biết nó để làm gì"! Những lập trình viên sử dụng những thứ không biết tại sao hầu như không được coi trọng nhất trong cuốn sách của tôi. :)
The Dag

Câu trả lời:


87

Từ Blog của Rico ...

Quy tắc 1

Đừng.

Đây thực sự là quy tắc quan trọng nhất. Công bằng mà nói rằng hầu hết các cách sử dụng GC.Collect () là một ý tưởng tồi và tôi đã đi sâu vào một số chi tiết trong bài đăng ban đầu nên tôi sẽ không lặp lại tất cả những điều đó ở đây. Vì vậy, hãy chuyển sang ...

Quy tắc số 2

Cân nhắc gọi GC.Collect () nếu một số sự kiện không lặp lại vừa xảy ra và sự kiện này có khả năng cao đã khiến nhiều đối tượng cũ chết.

Một ví dụ cổ điển về điều này là nếu bạn đang viết một ứng dụng khách và bạn hiển thị một biểu mẫu rất lớn và phức tạp có nhiều dữ liệu liên quan đến nó. Người dùng của bạn vừa tương tác với biểu mẫu này có khả năng tạo ra một số đối tượng lớn ... những thứ như tài liệu XML, hoặc một hoặc hai Tập dữ liệu lớn. Khi biểu mẫu đóng các đối tượng này đã chết và vì vậy GC.Collect () sẽ lấy lại bộ nhớ được liên kết với chúng ...

Vì vậy, có vẻ như tình huống này có thể thuộc Quy tắc số 2, bạn biết rằng có một thời điểm mà rất nhiều đồ vật cũ đã chết và nó không tái diễn. Tuy nhiên, đừng quên lời chia tay của Rico.

Quy tắc số 1 nên vượt trội hơn Quy tắc số 2 mà không có bằng chứng rõ ràng.

Đo, đo, đo.


9
Tôi muốn nói đây chỉ là điều cũ. Không có gì thực sự xấu hoặc nguy hiểm nếu bạn biết mình đang làm gì và do đó biết khi nào và làm như thế nào, cũng như tác dụng phụ của nó. Những điều như không bao giờ, không bao giờ sử dụng xxxx được đặt ở đó để bảo vệ thế giới khỏi các lập trình viên tệ hại: D
Jorge Córdoba


Tôi không nói rằng sử dụng GC.Collect là một phương pháp hay. Nhưng đôi khi nó là một cách nhanh chóng để giải quyết vấn đề mà không biết nguyên nhân thực sự của nó. Tôi biết là nó xấu xí, nhưng nó có hiệu quả, và đối với tôi nó có vẻ không phải là một cách tiếp cận tồi, đặc biệt là khi không có nhiều thời gian để tìm ra nguyên nhân sâu xa của vấn đề và sếp của bạn đang đứng sau lưng bạn ... bạn biết đấy.
Silent Sojourner

58

Nếu bạn gọi GC.Collect () trong mã sản xuất, về cơ bản bạn đang tuyên bố rằng bạn biết nhiều hơn các tác giả của GC. Đó có thể là trường hợp. Tuy nhiên nó thường không, và do đó rất không được khuyến khích.


3
Điều đó rất đúng, nhưng tôi không biết liệu họ có thể đưa ra các giả định áp dụng cho tất cả các diễn biến hay không.
MasterMastic

2
@Ken Không, họ không thể. Nhưng bạn có ở vị trí tốt hơn để làm như vậy không? Hay bạn sẽ viết mã giả sử phần cứng cụ thể, một phiên bản hệ điều hành cụ thể, v.v.? Tỷ lệ đau / tăng quá cao trên cái này.
The Dag

2
@TheDag IMO tất nhiên là tôi. Khi tôi giải phóng bộ nhớ và không có gì, tôi không thực sự quan tâm đến phần cứng vì đó là công việc của hệ điều hành để giải quyết điều đó. Tôi cũng không quan tâm đến hệ điều hành vì tôi có một giao diện chung cho tất cả các hệ điều hành mà tôi đang lập trình. (ví dụ: tôi không quan tâm đó là Windows, Mac hay Linux: khi tôi cấp phát / giải phóng bộ nhớ trong C / C ++ thì nó mới / xóa malloc / dealloc). Tôi luôn luôn có thể sai, vì vậy hãy thoải mái sửa chữa cho tôi.
MasterMastic

@MasterMastic mallocchỉ có một giao diện rất đơn giản và việc triển khai nó có thể khác nhau đến mức quan trọng. Tất cả phụ thuộc vào loại vấn đề bạn đang cố gắng giải quyết. Nếu malloc"đủ tốt", bạn sẽ không cần gộp bộ đệm, phải không? Quá trình phát triển C / C ++ có đầy đủ các ví dụ mà bạn cố gắng đoán thứ hai hệ điều hành / thời gian chạy / thư viện vì bạn biết rõ hơn (và đôi khi, bạn thực sự làm được). Nhiều ứng dụng quan trọng về hiệu suất tránh hoàn toàn sử dụng trình phân bổ hệ thống / thời gian chạy. Trò chơi được sử dụng để cấp phát trước tất cả bộ nhớ khi khởi động (các mảng có kích thước không đổi, v.v.).
Luaan 13/02/17

24

Vậy còn khi bạn đang sử dụng các đối tượng COM như MS Word hoặc MS Excel từ .NET thì sao? Nếu không gọi GC.Collectsau khi giải phóng các đối tượng COM, chúng tôi nhận thấy rằng các phiên bản ứng dụng Word hoặc Excel vẫn tồn tại.

Trên thực tế, mã chúng tôi sử dụng là:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

Vì vậy, đó có phải là một cách sử dụng không đúng của bộ thu gom rác? Nếu vậy làm thế nào để chúng ta làm cho các đối tượng Interop chết? Ngoài ra, nếu nó không được sử dụng như thế này, tại sao phương thức GCcủa là Collectthậm chí Public?


3
Điều này sẽ tạo ra một câu hỏi StackOverflow mới tuyệt vời, tức là: Làm thế nào để xóa các trường hợp COM mà không cần gọi GC. Đặc biệt liên quan đến các tham chiếu vòng tròn không được quản lý. Đó là một trong những thách thức khiến tôi phải cảnh giác khi nâng cấp phần bổ trợ VB6 Outlook của mình lên C #. (Chúng tôi đã làm rất nhiều việc để phát triển các mẫu mã và các trường hợp thử nghiệm ở phía VB mà đảm bảo rằng các tham chiếu COM đã bị giết theo kiểu xác định khi không còn cần thiết nữa).
rkagerer

2
Nếu điều này áp dụng cho các đối tượng COM nói chung, có lẽ đây là một kịch bản hợp lệ. Nhưng tôi muốn nói rằng vấn đề có thể là bạn đang sử dụng một ứng dụng khách được thiết kế cho một máy tính để bàn tương tác như một máy chủ COM. Từ cơ sở kiến ​​thức MSDN: "Microsoft hiện không khuyến nghị và không hỗ trợ Tự động hóa các ứng dụng Microsoft Office từ bất kỳ thành phần hoặc ứng dụng khách không được giám sát, không tương tác nào (bao gồm ASP, ASP.NET, DCOM và NT Services), vì Office có thể cho thấy hành vi không ổn định và / hoặc bế tắc khi Office được chạy trong môi trường này. "
The Dag

2
@TheDag - Microsoft có thể không khuyến nghị, nhưng nhiều người trong chúng ta đã phải chuyển mã VB6 cũ với tương tác văn phòng sang ứng dụng .Net windows. Tôi đã dành nhiều tháng làm việc cho đến khi tôi cuối cùng đã loại bỏ tất cả các tham chiếu treo vô hình cho một dự án chuyển đổi VB6 sang .Net lớn. Học cách phát hành theo thứ tự gán ngược và giữ các tham chiếu cục bộ cho MỌI đối tượng com đơn lẻ bao gồm cả các bộ sưu tập đã giúp ích.
Dib

15

Chà, GC là một trong những thứ mà tôi có mối quan hệ yêu / ghét. Chúng tôi đã từng phá vỡ nó trong quá khứ thông qua VistaDB và viết blog về nó. Họ đã sửa nó, nhưng phải mất một thời gian DÀI để nhận được bản sửa lỗi từ họ cho những thứ như thế này.

GC rất phức tạp và một kích thước phù hợp với tất cả các cách tiếp cận là rất, rất khó để đạt được thứ gì đó lớn như thế này. MS đã làm khá tốt nhiệm vụ của nó, nhưng đôi khi có thể đánh lừa GC.

Nói chung, bạn không nên thêm dấu Collecttrừ khi bạn biết thực tế là bạn vừa vứt bỏ một đống bộ nhớ và nó sẽ rơi vào tình trạng khủng hoảng giữa vòng đời nếu GC không được dọn dẹp ngay bây giờ.

Bạn có thể làm hỏng toàn bộ bộ máy với một loạt các GC.Collectcâu nói xấu . Sự cần thiết của một báo cáo thu thập hầu như luôn chỉ ra một lỗi cơ bản lớn hơn. Rò rỉ bộ nhớ thường liên quan đến các tài liệu tham khảo và thiếu hiểu biết về cách chúng hoạt động. Hoặc sử dụng các IDisposableđối tượng không cần nó và đặt tải cao hơn nhiều lên GC.

Theo dõi chặt chẽ% thời gian dành cho GC thông qua bộ đếm hiệu suất hệ thống. Nếu bạn thấy ứng dụng của mình sử dụng 20% ​​thời gian trở lên trong GC, tức là bạn đang gặp vấn đề nghiêm trọng về quản lý đối tượng (hoặc cách sử dụng bất thường). Bạn muốn luôn giảm thiểu thời gian GC bỏ ra vì nó sẽ tăng tốc toàn bộ ứng dụng của bạn.

Cũng cần lưu ý rằng GC trên máy chủ khác với máy trạm. Tôi đã thấy một số vấn đề nhỏ khó theo dõi khi mọi người không thử nghiệm cả hai (hoặc thậm chí không biết rằng chúng là hai trong số chúng).

Và chỉ để đầy đủ nhất trong câu trả lời của tôi, bạn cũng nên thử nghiệm trong Mono nếu bạn cũng đang nhắm mục tiêu nền tảng đó. Vì nó là một cách triển khai hoàn toàn khác, nó có thể gặp những vấn đề hoàn toàn khác với việc thực hiện MS.


Thủ phạm thường là các sự kiện. Bất cứ khi nào một phương thức thể hiện được sử dụng như một trình xử lý sự kiện, người phát hành sự kiện có một tham chiếu đến người đăng ký thông qua đại biểu sự kiện. Cách "dễ dàng" duy nhất để tránh các vấn đề từ việc này là chỉ sử dụng các nhà xuất bản có tuổi thọ cao nhất là người đăng ký (ví dụ: TextBox xuất bản một sự kiện được xử lý bởi biểu mẫu chứa thì không có vấn đề gì, vì hộp văn bản không được phép sống ngoài hình thức). Kịch bản bài toán ví dụ: Mô hình Singleton, các khung nhìn tạm thời xử lý các sự kiện của mô hình.
The Dag

5
Làm thế nào một người có thể vặn toàn bộ máy?
Adam R. Grey

13

Có những trường hợp nó hữu ích, nhưng nói chung nó nên tránh. Bạn có thể so sánh nó với GOTO, hoặc cưỡi xe mô tô: bạn làm điều đó khi bạn cần, nhưng bạn không nói với bạn bè về điều đó.


12

Theo kinh nghiệm của tôi, chưa bao giờ nên gọi GC.Collect () trong mã sản xuất. Trong gỡ lỗi, vâng, nó có lợi thế là giúp làm rõ các rò rỉ bộ nhớ tiềm ẩn. Tôi đoán lý do cơ bản của tôi là GC đã được viết và tối ưu hóa bởi các lập trình viên thông minh hơn nhiều so với tôi, và nếu tôi đến một điểm mà tôi cảm thấy tôi cần phải gọi GC. một vài nơi. Trong tình huống của bạn, có vẻ như bạn không thực sự có vấn đề về bộ nhớ, chỉ là bạn lo lắng về sự bất ổn định mà bộ sưu tập sẽ mang lại cho quá trình của bạn. Nhìn thấy rằng nó sẽ không làm sạch các đồ vật vẫn còn sử dụng, và nó thích ứng rất nhanh với cả nhu cầu tăng và giảm, tôi nghĩ bạn sẽ không phải lo lắng về điều đó.


10

Một trong những lý do lớn nhất để gọi GC.Collect () là khi bạn vừa thực hiện một sự kiện quan trọng tạo ra rất nhiều rác, chẳng hạn như những gì bạn mô tả. Gọi GC.Collect () có thể là một ý tưởng hay ở đây; nếu không, GC có thể không hiểu rằng đó là sự kiện 'một lần'.

Tất nhiên, bạn nên lập hồ sơ và tự mình xem.


9

Rõ ràng là bạn không nên viết mã với yêu cầu thời gian thực bằng các ngôn ngữ có tính năng thu gom rác không theo thời gian thực.

Trong trường hợp có các giai đoạn được xác định rõ ràng, không có vấn đề gì với việc kích hoạt bộ thu gom rác. Nhưng trường hợp này cực kỳ hiếm. Vấn đề là nhiều nhà phát triển sẽ cố gắng sử dụng điều này để giải quyết các vấn đề trên giấy tờ theo kiểu sùng bái hàng hóa và việc thêm nó một cách bừa bãi sẽ gây ra các vấn đề về hiệu suất.


Thật. Nhưng các thử nghiệm tự động có khả năng bắt được điều kiện lỗi "đối tượng không đủ điều kiện để thu gom rác, nhưng nên được" sẽ có giá trị. Tôi điều này có thể đạt được thông qua sự kết hợp của logic nhà máy, logic hủy và GC.Collect. Ví dụ: lớp Thực thể của bạn có thuộc tính IObjectTracker, thường là rỗng nhưng được gán bởi nhà máy thực thể có mục đích thử nghiệm. Nhà máy cũng thông báo cho bộ theo dõi sự ra đời của đối tượng, trong khi bộ hủy thông báo cho nó (khi có mặt) cái chết. Nếu bạn có thể biết "trình hủy đã thực thi cho tất cả các đối tượng thu gom rác", bạn có thể kiểm tra trạng thái trình theo dõi để phát hiện rò rỉ.
The Dag

7

Việc gọi GC.Collect () buộc CLR thực hiện bước đi trong ngăn xếp để xem liệu từng đối tượng có thể được giải phóng thực sự hay không bằng cách kiểm tra các tham chiếu. Điều này sẽ ảnh hưởng đến khả năng mở rộng nếu số lượng đối tượng nhiều và cũng đã được biết là kích hoạt thu gom rác quá thường xuyên. Tin cậy CLR và để bộ thu gom rác tự chạy khi thích hợp.


2
Bạn không chỉ gây ra việc đi bộ ngăn xếp, mà luồng chính của ứng dụng của bạn (và bất kỳ luồng con nào mà nó tạo ra) bị đóng băng để GC có thể đi bộ trong ngăn xếp. Ứng dụng của bạn dành càng nhiều thời gian trong GC, thì thời gian ứng dụng bị đóng băng càng nhiều.
Scott Dorman 23/09/08

3
Tôi lo ngại về sự cố Ứng dụng do ngoại lệ Hết bộ nhớ hơn là hiệu suất chậm vì ứng dụng / GC đã loại bỏ những thứ không còn cần thiết. Có ai biết tại sao Microsoft xuất hiện để ném ngoại lệ OOM mà không ĐẦU TIÊN vứt rác không? (Nếu không có bước OBVIOUS này - hoặc ít nhất là giải thích lý do tại sao bước này dường như không được cố gắng thực hiện trước khi đưa ra ngoại lệ OOM Tôi không chắc mình có bất kỳ niềm tin nào vào những thứ đang xảy ra "tự động" theo "cách mà chúng phải xảy ra".
Wonderbird

6

Trên thực tế, tôi không nghĩ rằng việc gọi GC.Collect là một cách thực hành quá tệ.
Có thể có những trường hợp chúng ta cần điều đó. Ví dụ: tôi có một biểu mẫu chạy một chuỗi, inturn sẽ mở các bảng khác nhau trong cơ sở dữ liệu, trích xuất nội dung trong trường BLOB thành tệp tạm thời, mã hóa tệp, sau đó đọc tệp đó thành dòng nhị phân và quay lại BLOB trường trong bảng khác.

Toàn bộ hoạt động chiếm khá nhiều bộ nhớ và không chắc chắn về số hàng và kích thước của nội dung tệp trong bảng.

Tôi đã từng thường xuyên nhận được OutofMemory Exception và tôi nghĩ sẽ là khôn ngoan nếu chạy GC.Collect định kỳ dựa trên một biến bộ đếm. Tôi tăng một bộ đếm và khi đạt đến một mức cụ thể, GC được gọi để thu thập bất kỳ rác nào có thể đã hình thành và để lấy lại bất kỳ bộ nhớ nào bị mất do rò rỉ bộ nhớ không lường trước được.

Sau đó, tôi nghĩ rằng nó đang hoạt động tốt, ít nhất là không có ngoại lệ !!!
Tôi gọi theo cách sau:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

5

Theo .net, thời gian cần thiết để thực hiện thu gom rác liên quan nhiều đến số lượng nội dung không phải là rác, hơn là số lượng nội dung đó. Thật vậy, trừ khi một đối tượng ghi đè Finalize(hoặc rõ ràng hoặc thông qua trình hủy C #), là đích của a WeakReference, nằm trên Large Object Heap, hoặc đặc biệt theo một số cách khác liên quan đến gc, điều duy nhất xác định bộ nhớ mà nó nằm trong đó vì là một đối tượng là sự tồn tại của các tham chiếu gốc đến nó. Mặt khác, hoạt động của GC cũng tương tự như lấy từ một tòa nhà mọi thứ có giá trị và khởi động tòa nhà, xây dựng một tòa nhà mới trên địa điểm của tòa nhà cũ và đặt tất cả các vật phẩm có giá trị vào đó. Nỗ lực cần thiết để khởi động tòa nhà hoàn toàn không phụ thuộc vào lượng rác bên trong nó.

Do đó, việc gọi GC.Collectlà phù hợp để tăng tổng lượng công việc mà hệ thống phải thực hiện. Nó sẽ trì hoãn sự xuất hiện của bộ sưu tập tiếp theo, nhưng có thể sẽ thực hiện ngay lập tức nhiều công việc như bộ sưu tập tiếp theo sẽ yêu cầu khi nó xảy ra; tại thời điểm mà lần thu thập tiếp theo sẽ xảy ra, tổng thời gian dành cho việc thu thập sẽ tương đương với thời gian GC.Collectchưa được gọi, nhưng hệ thống sẽ tích lũy một số rác, khiến việc thu gom tiếp theo được yêu cầu sớm hơn GC.Collectkhông. đã được gọi.

Những thời điểm tôi có thể thấy GC.Collectthực sự hữu ích là khi người ta cần đo mức sử dụng bộ nhớ của một số mã (vì số liệu sử dụng bộ nhớ chỉ thực sự có ý nghĩa sau một bộ sưu tập) hoặc cấu hình của một số thuật toán tốt hơn (gọi GC.Collect () trước khi chạy từng đoạn mã có thể giúp đảm bảo trạng thái cơ sở nhất quán). Có một số trường hợp khác mà người ta có thể biết những thứ mà GC không, nhưng trừ khi người ta đang viết một chương trình đơn luồng, không có cách nào người ta có thể biết rằng một GC.Collectlệnh gọi sẽ giúp cấu trúc dữ liệu của một luồng tránh "khủng hoảng giữa vòng đời "sẽ không khiến dữ liệu của các chuỗi khác có" khủng hoảng giữa vòng đời "mà lẽ ra phải tránh được.


5

Tạo hình ảnh theo vòng lặp - ngay cả khi bạn gọi vứt bỏ, bộ nhớ vẫn không được phục hồi. Mỗi lần thu gom rác. Tôi đã chuyển từ bộ nhớ 1,7GB trên ứng dụng xử lý ảnh của mình lên 24MB và hiệu suất rất tuyệt vời.

Hoàn toàn có thời gian mà bạn cần gọi cho GC.Collect.


2
Gọi Disposekhông được phép giải phóng bộ nhớ được quản lý. Bạn dường như không biết mô hình bộ nhớ trong .NET hoạt động như thế nào.
Andrew Barber

4

Chúng tôi đã gặp sự cố tương tự với trình thu gom rác không thu gom rác và giải phóng bộ nhớ.

Trong chương trình của chúng tôi, chúng tôi đang xử lý một số Bảng tính Excel có kích thước khiêm tốn bằng OpenXML. Các bảng tính chứa từ 5 đến 10 "trang tính" với khoảng 1000 hàng 14 cột.

Chương trình trong môi trường 32 bit (x86) sẽ bị lỗi với lỗi "hết bộ nhớ". Chúng tôi đã làm cho nó chạy trong môi trường x64, nhưng chúng tôi muốn có một giải pháp tốt hơn.

Chúng tôi đã tìm thấy một.

Dưới đây là một số đoạn mã được đơn giản hóa về những gì không hoạt động và những gì đã hoạt động khi nói đến việc gọi Trình thu gom rác một cách rõ ràng để giải phóng bộ nhớ khỏi các đối tượng bị xử lý.

Việc gọi GC từ bên trong chương trình con không hoạt động. Ký ức không bao giờ được lấy lại ...

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

Bằng cách Di chuyển lệnh gọi GC ra ngoài phạm vi của chương trình con, rác đã được thu thập và giải phóng bộ nhớ.

For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

Tôi hy vọng điều này sẽ giúp những người khác đang thất vọng với bộ sưu tập rác .NET khi nó dường như bỏ qua các lệnh gọi tới GC.Collect().

Paul Smith


4

Không có gì sai khi gọi một bộ sưu tập một cách rõ ràng Một số người chỉ thực sự muốn tin rằng nếu đó là một dịch vụ được cung cấp bởi nhà cung cấp, đừng thắc mắc về điều đó. Ồ, và tất cả những lần đóng băng ngẫu nhiên đó vào những thời điểm không chính xác của ứng dụng tương tác của bạn? Phiên bản tiếp theo sẽ làm cho nó tốt hơn!

Để một quá trình nền xử lý thao tác bộ nhớ có nghĩa là không phải tự mình xử lý nó, đúng như vậy. Nhưng về mặt logic, điều này không có nghĩa là tốt nhất chúng ta không nên tự mình giải quyết trong mọi trường hợp. GC được tối ưu hóa cho hầu hết các trường hợp. Nhưng về mặt logic, điều này không có nghĩa là nó được tối ưu hóa trong mọi trường hợp.

Bạn đã bao giờ trả lời một câu hỏi mở chẳng hạn như 'thuật toán sắp xếp tốt nhất' với câu trả lời dứt khoát chưa? Nếu vậy, đừng chạm vào GC. Đối với những người bạn đã hỏi các điều kiện hoặc đưa ra câu trả lời kiểu 'trong trường hợp này', bạn có thể tiếp tục tìm hiểu về GC và thời điểm kích hoạt nó.

Phải nói rằng, tôi đã có ứng dụng bị đóng băng trong Chrome và Firefox khiến tôi bực bội và thậm chí sau đó trong một số trường hợp, bộ nhớ phát triển không bị cản trở - Giá như họ học cách gọi người thu gom rác - hoặc cho tôi một để khi bắt đầu đọc văn bản của trang, tôi có thể nhấn vào nó và do đó không bị treo trong 20 phút tiếp theo.


2

Tôi nghĩ rằng bạn đúng về kịch bản, nhưng tôi không chắc về API.

Microsoft nói rằng trong những trường hợp như vậy, bạn nên thêm áp lực bộ nhớ như một gợi ý cho GC rằng nó sẽ sớm thực hiện thu thập.


2
Thật thú vị, nhưng tài liệu nói rằng AddMemoryPressure nên được sử dụng khi 'một đối tượng được quản lý nhỏ phân bổ một lượng lớn bộ nhớ không được quản lý'. (tôi nhấn mạnh)
Robert Paulson

2

Có gì sai với nó? Thực tế là bạn đang đoán thứ hai về bộ thu gom rác và bộ cấp phát bộ nhớ, giữa chúng có ý tưởng lớn hơn nhiều về việc sử dụng bộ nhớ thực tế của ứng dụng trong thời gian chạy hơn bạn.


1
Bản chất heuristic của bộ thu gom rác và thực tế là họ đã đưa chức năng này ra thế giới bên ngoài khiến tôi nghĩ rằng nó như một thứ hữu ích nếu được sử dụng ở những nơi cần thiết. Vấn đề không phải là sử dụng nó mà là biết sử dụng nó như thế nào, ở đâu và khi nào.
Bẫy

Chưa kể các GC có kiến ​​thức tốt hơn về mọi ứng dụng khác và nhu cầu bộ nhớ của họ. GC thương lượng bộ nhớ với HĐH và như vậy sẽ bị ảnh hưởng bởi bộ nhớ vật lý khả dụng và tất cả các quy trình khác trên máy cả được quản lý và không được quản lý. Mặc dù tôi nghi ngờ GC thực sự biết "khi nào là thời điểm thích hợp để thu thập" trên cơ sở "từng trường hợp cụ thể", nhưng nó rất có thể có một chiến lược tổng thể tốt hơn so với ... BẤT KỲ ứng dụng đơn lẻ nào. ;)
The Dag

2

Mong muốn gọi GC.Collect () thường là cố gắng che đậy những sai lầm bạn đã mắc phải ở một nơi khác!

Sẽ tốt hơn nếu bạn tìm thấy nơi bạn quên vứt bỏ những thứ bạn không cần nữa.


5
thats có lẽ là một sự tổng quát
MickyD

1

Tóm lại, bạn có thể lập hồ sơ ứng dụng và xem những bộ sưu tập bổ sung này ảnh hưởng đến mọi thứ như thế nào. Tuy nhiên, tôi khuyên bạn nên tránh xa nó trừ khi bạn định xem hồ sơ. GC được thiết kế để tự chăm sóc và khi thời gian chạy phát triển, chúng có thể tăng hiệu quả. Bạn không muốn một loạt mã treo xung quanh có thể làm xáo trộn công việc và không thể tận dụng những cải tiến này. Có một lập luận tương tự cho việc sử dụng foreach thay vì for, đó là, các cải tiến trong tương lai dưới lớp vỏ có thể được thêm vào foreach và mã của bạn không cần phải thay đổi để tận dụng.


1

Bản thân .NET Framework chưa bao giờ được thiết kế để chạy trong môi trường thời gian thực. Nếu bạn thực sự cần xử lý thời gian thực, bạn sẽ sử dụng ngôn ngữ thời gian thực được nhúng không dựa trên .NET hoặc sử dụng .NET Compact Framework chạy trên thiết bị Windows CE.


Anh ta có thể đang sử dụng .Net Micro Framework, được thiết kế cho môi trường thời gian thực.
TraumaPony 23/09/08

@TraumaPony: Kiểm tra biểu đồ ở cuối trang này msdn.microsoft.com/en-us/embedded/bb278106.aspx : Rõ ràng là Micro Framework không được thiết kế cho môi trường thời gian thực. Tuy nhiên, nó được thiết kế cho các môi trường nhúng (như WinCE) nhưng với yêu cầu điện năng thấp hơn.
Scott Dorman 23/09/08

1

Điều tồi tệ nhất mà nó sẽ làm là làm cho chương trình của bạn bị đóng băng một chút. Vì vậy, nếu điều đó là OK với bạn, hãy làm điều đó. Thông thường nó không cần thiết cho các ứng dụng web hoặc ứng dụng khách dày đặc với sự tương tác chủ yếu của người dùng.

Tôi nhận thấy rằng đôi khi các chương trình có luồng chạy dài hoặc chương trình hàng loạt sẽ nhận được ngoại lệ OutOfMemory mặc dù chúng đang sắp xếp các đối tượng đúng cách. Một điều tôi nhớ là xử lý giao dịch cơ sở dữ liệu ngành nghề; còn lại là quy trình lập chỉ mục trên một chuỗi nền trong một ứng dụng khách dày.

Trong cả hai trường hợp, kết quả rất đơn giản: Không có GC.Collect, hết bộ nhớ, nhất quán; GC.Thu thập, hiệu suất hoàn hảo.

Tôi đã thử nó để giải quyết các vấn đề về bộ nhớ vài lần khác, nhưng không có kết quả. Tôi lấy nó ra.

Tóm lại, đừng đưa nó vào trừ khi bạn đang gặp lỗi. Nếu bạn đặt nó vào và nó không khắc phục được sự cố bộ nhớ, hãy lấy nó ra. Hãy nhớ kiểm tra ở chế độ Phát hành và so sánh táo với táo.

Lần duy nhất mọi thứ có thể trở nên sai trái với điều này là khi bạn nhận thức được về mặt đạo đức. Nó không phải là một vấn đề giá trị; nhiều lập trình viên đã chết và đi thẳng lên thiên đường với nhiều bộ sưu tập GC.C không cần thiết trong mã của họ, có tuổi thọ cao hơn họ.

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.