2013-02-22 34 views
12

Tôi gặp một số vấn đề với dịch vụ web WCF (một số bãi chứa, rò rỉ bộ nhớ, v.v.) và tôi chạy công cụ profillng (ANTS Memory Profiles).Bộ nhớ giao dịch từ từ điển C# chứa trong một đối tượng tĩnh

Chỉ cần tìm hiểu rằng ngay cả khi xử lý qua (tôi chạy một thử nghiệm cụ thể và sau đó dừng lại), Thế hệ 2 là 25% bộ nhớ cho dịch vụ web. Tôi đã theo dõi bộ nhớ này để tìm thấy rằng tôi đã có một đối tượng từ điển đầy đủ các mục (null, null), với -1 mã băm.

Luồng công việc của dịch vụ web ngụ ý rằng trong quá trình xử lý các mục cụ thể được thêm vào và sau đó bị xóa khỏi từ điển (chỉ đơn giản AddRemove). Không phải là một thỏa thuận lớn. Nhưng có vẻ như sau khi tất cả các mục được xóa, từ điển đầy (null, null) KeyValuePair s. Hàng ngàn người trong số họ trong thực tế, như vậy mà họ chiếm một phần lớn của bộ nhớ và cuối cùng tràn xảy ra, với các ứng dụng buộc tương ứng hồ bơi tái chế và DW20.exe nhận được tất cả các chu kỳ CPU nó có thể nhận được.

Từ điển trên thực tế là Dictionary<SomeKeyType, IEnumerable<KeyValuePair<SomeOtherKeyType, SomeCustomType>>> (System.OutOfMemoryException because of Large Dictionary) vì vậy, tôi đã kiểm tra xem có loại tham chiếu nào đang nắm giữ mọi thứ không.

Từ điển được chứa trong một đối tượng tĩnh (để làm cho nó có thể xử lý được với các xử lý khác nhau thông qua quá trình xử lý), từ câu hỏi này và nhiều hơn nữa (Do static members ever get garbage collected?) Tôi hiểu tại sao từ điển đó là Thế hệ 2. Nhưng đây cũng là nguyên nhân của những người (null, null)? Ngay cả khi tôi xóa các mục khỏi từ điển, thứ gì đó sẽ luôn bị chiếm giữ trong bộ nhớ?

Đây không phải là vấn đề tốc độ như trong câu hỏi này Deallocate memory from large data structures in C#. Dường như bộ nhớ không bao giờ được khai hoang.

Có điều gì tôi có thể thực hiện để thực sự xóa các mục khỏi từ điển, không chỉ tiếp tục lấp đầy nó bằng các cặp (null, null)? Có điều gì khác mà tôi cần phải kiểm tra không?

+0

+ !: Để nghiên cứu thích hợp và viết một câu hỏi hay. – Virtlink

+1

'Danh sách <>' có phương thức 'TrimExcess()', 'Từ điển <,>' không may là không. Nghĩ rằng đây là một số điểm dễ dàng :) –

+0

Gọi từ điển.Clear() có tạo nên sự khác biệt nào không? – Alex

Trả lời

9

Từ điển lưu trữ các mục trong bảng băm. Một mảng được sử dụng nội bộ cho việc này. Bởi vì cách các bảng băm hoạt động, mảng này phải luôn lớn hơn số lượng thực tế của các mục được lưu trữ (ít nhất là khoảng 30% lớn hơn). Microsoft sử dụng hệ số tải 72%, tức là ít nhất 28% mảng sẽ trống (xem An Extensive Examination of Data Structures Using C# 2.0 và đặc biệt là The System.Collections.Hashtable ClassThe System.Collections.Generic.Dictionary Class) Vì vậy, các mục nhập rỗng/rỗng chỉ có thể đại diện cho không gian trống này.

Nếu mảng quá nhỏ, nó sẽ tự động phát triển; tuy nhiên, khi các mục được gỡ bỏ, mảng không co lại, nhưng không gian sẽ được giải phóng sẽ được tái sử dụng khi các mục mới được chèn vào.

Nếu bạn đang ở trong kiểm soát của từ điển này, bạn có thể cố gắng tái tạo nó để thu nhỏ nó:

theDict = new Dictionary<TKey, IEnumerable<KeyValuePair<TKey2, TVal>>>(theDict); 

Nhưng vấn đề có thể phát sinh từ các mục (không có sản phẩm nào) thực tế. Từ điển của bạn là tĩnh và do đó sẽ không bao giờ được khai thác tự động bởi bộ thu gom rác, trừ khi bạn gán cho nó một từ điển khác hoặc null (theDict = new ... hoặc theDict = null).Điều này chỉ đúng đối với chính từ điển đó là tĩnh, không phải cho các mục nhập của nó. Miễn là các tham chiếu đến các mục đã xóa tồn tại ở một nơi khác, chúng sẽ vẫn tồn tại. GC sẽ lấy lại bất kỳ đối tượng nào (trước đó hoặc sau này) mà không thể truy cập được nữa thông qua một số tham chiếu. Nó làm cho không có sự khác biệt, cho dù đối tượng này đã được tuyên bố tĩnh hay không. Bản thân các đối tượng không tĩnh, chỉ có các tham chiếu của chúng.

+0

Cảm ơn bạn đã cung cấp thông tin, mọi thứ trở nên rõ ràng hơn cho tôi. Mặc dù tôi thấy những ưu điểm của bộ nhớ trống miễn phí, tôi vẫn muốn 'thu nhỏ' theo cách bạn và @usr đề xuất. Tôi chỉ phải phân tích tốt hơn khi tôi muốn. –

+0

@Olivier Bằng cách nói nó không co lại khi các mục bị xóa, điều đó có nghĩa là bộ nhớ vẫn sẽ được sử dụng? –

+0

@Siraj Mansour: Có. –

4

Có vẻ như bạn cần tái chế không gian trong dict đó theo định kỳ. Bạn có thể làm điều đó bằng cách tạo một cái mới: new Dictionary<a,b>(oldDict). Hãy chắc chắn để làm điều này một cách an toàn thread.

Khi nào cần thực hiện việc này? Hoặc trên đánh dấu của một bộ đếm thời gian (60 giây?) Hoặc khi một số lượng cụ thể của viết đã xảy ra (100k?) (Bạn cần phải giữ một bộ đếm sửa đổi).

0

Một giải pháp có thể là gọi phương thức Clear() trên từ điển tĩnh. Bằng cách này, tham chiếu đến từ điển sẽ vẫn còn, nhưng các đối tượng chứa sẽ được giải phóng.

+0

Ai đó đã đề cập đến điều này trong các bình luận, và tôi đã trả lời tại thời điểm đó rằng việc sử dụng phương pháp 'Clear' không dẫn đến những gì tôi muốn. –

-2

Điều này tương tự như sự cố tôi đã tìm thấy khi xử lý các đối tượng DataTable lớn trong ứng dụng DB một thời gian trước đây. Đơn giản chỉ cần gọi Clear trên DataTable đã để lại tất cả các hàng trong bộ nhớ với các giá trị null. Vì vậy, chúng tôi thực hiện một phương pháp thanh toán bù trừ cụ thể như:

someTable.Clear(); 
someTable.Dispose(); 
someTeble = new DataTable()... 

với bước mở rộng thêm các cột ngay trở lại vào mới trống, sạch sẽ, DataTable.

Lời nguyền trong này là ofc rằng chúng tôi đã có rất nhiều mã tham chiếu someTable đã được xử lý và mã không trỏ đến phiên bản mới. Big mess.

Tôi đoán là dưới mui xe, đây là những đối tượng không được quản lý với trình bao bọc .net ở đầu chúng và bộ nhớ chỉ được giải phóng khi đối tượng chính bị hủy.

Vì vậy, nếu bạn đang sử dụng chúng một cách năng động, bạn phải tự làm sạch bộ nhớ.

Tôi vừa chạy vào bài đăng đó vì tôi thấy cùng vấn đề với hashtables.

+0

Điều này không trả lời được câu hỏi – Liam

Các vấn đề liên quan