2009-05-30 31 views
14

Tôi có bộ nhớ cache sử dụng WeakReferences cho các đối tượng được lưu trong bộ nhớ cache để làm cho chúng tự động bị xóa khỏi bộ nhớ cache trong trường hợp áp lực bộ nhớ. Vấn đề của tôi là các đối tượng được lưu trữ được thu thập rất sớm sau khi chúng được lưu trữ trong bộ nhớ cache. Bộ nhớ cache chạy trong một ứng dụng 64-bit và mặc dù trường hợp có nhiều hơn 4gig bộ nhớ vẫn còn, tất cả các đối tượng được lưu trữ được thu thập (chúng thường được lưu trữ trong G2-heap tại thời điểm đó). Không có bộ sưu tập rác gây ra bằng tay như trình thám hiểm quá trình cho thấy.WeakReference có tạo bộ nhớ cache tốt không?

Tôi có thể áp dụng những phương pháp nào để làm cho các đối tượng này tồn tại lâu hơn?

Trả lời

3

Không, WeakReference không tốt cho điều đó vì hành vi của bộ thu gom rác có thể và sẽ thay đổi theo thời gian và bộ nhớ cache của bạn không nên dựa vào hành vi ngày nay. Ngoài ra nhiều yếu tố ngoài tầm kiểm soát của bạn có thể ảnh hưởng đến áp lực bộ nhớ.

Có nhiều triển khai bộ nhớ cache cho .NET. Bạn có thể tìm thấy một tá trên CodePlex. Tôi đoán những gì bạn cần phải thêm vào nó là cái gì đó nhìn vào bộ làm việc hiện tại của ứng dụng để sử dụng nó như là một kích hoạt cho tẩy.

Một lưu ý khác về lý do tại sao đối tượng của bạn được thu thập thường xuyên. GC rất tích cực trong việc làm sạch các đối tượng Gen0. Nếu các đối tượng của bạn rất ngắn ngủi (cho đến khi tham chiếu duy nhất đến nó là một tham chiếu yếu) thì GC đang làm những gì nó được thiết kế để làm bằng cách dọn dẹp càng nhanh càng tốt.

+3

"một tá trên CodePlex" ... và một trong Khung. ASP.NET Cache System.Web.Caching.Cache có thể được sử dụng trong các ứng dụng non -ASP.NET và khá mạnh mẽ. Microsoft tài liệu nói rằng nó không được khuyến cáo sử dụng nó trong các ứng dụng của khách hàng (nhưng không thực sự nói lý do tại sao), nhưng tôi đã sử dụng nó trong một loạt các ứng dụng thành công. – Joe

+1

Tôi đã thử HttpRuntime.Cache, nhưng nó không hoạt động như tôi muốn.Tôi liên tục thêm các mục vào bộ nhớ cache và sớm nhận được một OutOfMemoryException thay vì có các mục gỡ bỏ bộ nhớ cache. – Rauhotz

+0

"HttpRuntime.Cache, nhưng nó không hoạt động như tôi thích ... OutOfMemoryException". Đáng ngạc nhiên, nhưng bạn có thể thử điều chỉnh cấu hình bộ nhớ cache - ví dụ: privateBytesLimit và percentPhysicalMemoryUsedLimit properties. Hoặc có thể có một số lý do khác cho OutOfMemoryException. – Joe

14

Sử dụng tham chiếu yếu là phương tiện chính để tham chiếu các đối tượng được lưu trong bộ nhớ cache thực sự không phải là một ý tưởng tuyệt vời, bởi vì như Josh đã nói, bạn không thể thay đổi hành vi của WeakReference và GC.

Tuy nhiên, nếu bộ nhớ cache của bạn cần bất kỳ loại khả năng phục sinh nào, việc sử dụng WeakReferences cho các mục đang chờ xử lý sẽ hữu ích. Khi một mặt hàng đáp ứng tiêu chuẩn trục xuất, thay vì ngay lập tức đuổi nó, bạn thay đổi tham chiếu của nó thành một tham chiếu yếu. Nếu bất cứ điều gì yêu cầu nó trước khi nó được GC'ed, bạn khôi phục tài liệu tham khảo mạnh mẽ của nó, và các đối tượng có thể sống một lần nữa. Tôi đã tìm thấy điều này hữu ích cho một số bộ nhớ cache có khó dự đoán các mẫu tỷ lệ hit với thường xuyên đủ "phục sinh" để có lợi.

Nếu bạn có các mẫu tỷ lệ lần truy cập có thể dự đoán được, thì tôi sẽ từ bỏ tùy chọn WeakReference và thực hiện các hành vi rõ ràng.

+0

Vấn đề là ngay sau khi tôi có một tham chiếu mạnh mẽ một đối tượng được lưu trữ, tôi đang gặp nguy cơ chạy hết bộ nhớ trước khi tôi có thể thiết lập nó như là thu. – Rauhotz

+2

Đó là công việc của chiến lược trục xuất của bạn. Mỗi bộ nhớ cache cần hai thứ chính ... khả năng đăng ký một cá thể trong bộ nhớ cache và một quá trình nền xác định các mục nào sẽ được loại bỏ dựa trên một bộ quy tắc. Có rất nhiều chiến lược trục xuất hiện tại có thể hoạt động: LRU (Ít nhất được sử dụng gần đây ... tức là cũ nhất), LFU (Ít nhất được sử dụng thường xuyên ... tức là 10 lượt truy cập bị rơi vào lợi thế của các mục có 100 lần truy cập), v.v. 't bộ nhớ cache mỗi đối tượng được thêm vào mãi mãi ... bạn phải thêm một chủ đề nền để xử lý việc trục xuất. – jrista

+0

Nhưng chủ đề nền đó sẽ dọn sạch bộ nhớ cache chỉ sau đó và cuối cùng là quá muộn. – Rauhotz

5

Trong .net, WeakReference không được coi là tham chiếu từ quan điểm GC, vì vậy, bất kỳ đối tượng nào chỉ có tham chiếu yếu sẽ được thu thập trong lần chạy GC tiếp theo (cho thế hệ tương ứng).

Điều đó làm cho tham chiếu yếu hoàn toàn không phù hợp cho bộ nhớ đệm - như trải nghiệm của bạn hiển thị.

Bạn cần một thành phần bộ nhớ cache "thực" và điều quan trọng nhất về bộ đệm là để có được chính sách gỡ bỏ (tức là quy tắc về thời điểm thả đối tượng khỏi bộ nhớ cache) phù hợp nhất với bạn mẫu sử dụng của ứng dụng.

5

Có một tình huống trong đó bộ nhớ đệm dựa trên WeakReference có thể tốt: khi tính hữu ích của một mục trong lớp được xác định dựa trên sự tồn tại của tham chiếu đến nó. Trong tình huống như vậy, bộ đệm ẩn yếu có thể hữu ích. Ví dụ, nếu một người có một ứng dụng mà sẽ deserialize nhiều đối tượng bất biến lớn, nhiều người trong số đó đã được dự kiến ​​sẽ được trùng lặp, và sẽ phải thực hiện nhiều so sánh giữa chúng. Nếu XY là tham chiếu đến một số loại lớp bất biến, kiểm tra X.Equals(Y) sẽ rất nhanh nếu cả hai biến trỏ đến cùng một trường hợp, nhưng có thể rất chậm nếu chúng trỏ đến các trường hợp riêng biệt xảy ra bằng nhau.Nếu một đối tượng deserialized xảy ra để phù hợp với một đối tượng khác mà một tham chiếu đã tồn tại, tìm nạp từ điển một tham chiếu đến đối tượng thứ hai (yêu cầu một so sánh chậm) có thể tiến hành so sánh trong tương lai. Mặt khác, nếu nó phù hợp với một mục trong từ điển nhưng từ điển chỉ là chỉ tham chiếu cho mục đó, sẽ có ít lợi thế khi sử dụng đối tượng từ điển thay vì chỉ giữ đối tượng đã đọc; có lẽ không đủ lợi thế để biện minh cho chi phí so sánh. Đối với bộ đệm ẩn, có WeakReferences bị vô hiệu hóa càng sớm càng tốt khi không có tham chiếu khác tồn tại cho đối tượng sẽ là một điều tốt.

0

Câu trả lời thực sự phụ thuộc vào đặc điểm sử dụng của bộ nhớ cache mà bạn đang cố gắng xây dựng. Tôi đã sử dụng thành công chiến lược bộ nhớ đệm dựa trên WeakReference để cải thiện hiệu suất trong nhiều dự án của tôi, nơi các đối tượng được lưu trữ dự kiến ​​sẽ được sử dụng trong các lần đọc nhiều lần. Như những người khác đã chỉ ra, các tài liệu tham khảo yếu là khá nhiều rác từ quan điểm của GC và sẽ được thu thập bất cứ khi nào chu kỳ GC tiếp theo được chạy. Nó không liên quan gì đến việc sử dụng bộ nhớ.

Nếu, tuy nhiên, bạn cần một bộ nhớ cache tồn tại sự tàn bạo như vậy từ GC, bạn cần sử dụng hoặc bắt chước chức năng được cung cấp bởi không gian tên System.Runtime.Caching. Hãy nhớ rằng bạn cần một chuỗi bổ sung để dọn sạch bộ nhớ cache khi sử dụng bộ nhớ vượt qua ngưỡng của bạn.

1

Tôi tin rằng vấn đề bạn đang gặp là Collector rác xóa bỏ các yếu tham chiếu đối tượng trong phản ứng không chỉ để đáp ứng với áp lực bộ nhớ - thay vào đó nó sẽ làm bộ sưu tập khá tích cực đôi khi chỉ vì hệ thống runtime nghĩ một số đối tượng có thể có khả năng đã trở thành không thể truy cập được.

Bạn có thể sử dụng tốt hơn ví dụ: System.Runtime.Caching.MemoryCache có thể được cấu hình với giới hạn bộ nhớ hoặc chính sách gỡ bỏ tùy chỉnh cho các mục.

0

Một hơi muộn, nhưng đây là một trường hợp sử dụng có liên quan:

tôi cần phải bộ nhớ cache hai loại đối tượng: lớn (deserialised) tập tin dữ liệu mà mất 10 phút để tải và 15G chi phí của ram mỗi, và nhỏ hơn (động được biên dịch) các đối tượng chứa tham chiếu nội bộ cho các tệp dữ liệu đó (các đối tượng nhỏ hơn cũng được lưu trong bộ nhớ cache vì chúng mất ~ 10 giây để tạo). Các kho lưu trữ này được ẩn trong các nhà máy cung cấp các đối tượng (thành phần cũ không có kiến ​​thức về các đối tượng này) và có các chính sách trục xuất khác nhau.

Khi bộ nhớ cache `dữ liệu 'của tôi gợi lên một đối tượng, nó thay thế nó bằng tham chiếu yếu, vì vậy nếu đối tượng đó vẫn có sẵn khi được yêu cầu tiếp theo, chúng tôi có thể phục hồi lại (và gia hạn thời gian chờ bộ nhớ cache). Bằng cách này, chúng tôi tránh mất (hoặc vô tình nhân đôi) bất kỳ đối tượng nào trước khi nó thực sự không còn tồn tại (tức là không được sử dụng ở bất kỳ nơi nào khác). Lưu ý rằng không yêu cầu bộ nhớ cache để nhận thức được bộ nhớ khác và không có đối tượng khách hàng nào khác cần lưu ý rằng có bất kỳ bộ đệm nào (ví dụ: chúng ta tránh cần các 'keepalives', callbacks, registration, retrieve-and-return scope , v.v. - mọi thứ trở nên đơn giản hơn rất nhiều). Vì vậy, mặc dù sử dụng WeakReference (thay vì cache) là một ý tưởng khủng khiếp (vì GC hiện đại thường được điều chỉnh theo kích thước của cache L2 CPU, và mã thông thường sẽ ghi qua nhiều lần trong một phút), đó là rất hữu ích như một cách để ẩn bộ nhớ cache của bạn từ phần còn lại của mã.

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