2009-02-17 37 views
13

Tôi đã nghe và đọc rằng một chuỗi không thể thay đổi (không thay đổi?). Điều đó đúng là tôi đoán. Nhưng tôi cũng đã nghe nói rằng hai chuỗi với cùng một nội dung chia sẻ cùng một không gian bộ nhớ (hoặc những gì bạn gọi nó). Điều này có đúng không? Và nếu vậy, điều đó có nghĩa là nếu tôi tạo ra một Danh sách có hàng nghìn chuỗi, nó sẽ không thực sự chiếm nhiều không gian nếu hầu hết các chuỗi đó bằng nhau không? Không phải vậy.C#: Các chuỗi có cùng nội dung

Trả lời

18

EDIT: Trong câu trả lời dưới đây tôi đã gọi hồ bơi thực tập là đặc thù của AppDomain; Tôi khá chắc chắn đó là những gì tôi đã quan sát trước đây, nhưng các tài liệu MSDN cho String.Intern cho thấy rằng có một hồ bơi duy nhất cho toàn bộ quá trình, làm cho điều này thậm chí còn quan trọng hơn.

Original câu trả lời

(Tôi đã đi để thêm video này như một lời nhận xét, nhưng tôi nghĩ đó là một điểm quan trọng, đủ để cần một câu trả lời thêm ...)

Như những người khác đã giải thích, chuỗi interning xảy ra cho tất cả các xâu, nhưng không phải trên dây "tự động tạo ra" (ví dụ như những người đọc từ một cơ sở dữ liệu hoặc tập tin, hoặc xây dựng sử dụng StringBuilder hay String.Format.)

Tuy nhiên, tôi sẽ không đề nghị c alling String.Intern để làm tròn điểm thứ hai: nó sẽ điền vào hồ bơi thực tập trong suốt thời gian AppDomain của bạn. Thay vào đó, hãy sử dụng hồ bơi địa phương chỉ để sử dụng của bạn.Dưới đây là một ví dụ về một hồ bơi như:

public class StringPool 
{ 
    private readonly Dictionary<string,string> contents = 
     new Dictionary<string,string>(); 

    public string Add(string item) 
    { 
     string ret; 
     if (!contents.TryGetValue(item, out ret)) 
     { 
      contents[item] = item; 
      ret = item; 
     } 
     return ret; 
    } 
} 

Bạn muốn sau đó chỉ cần sử dụng một cái gì đó như:

string data = pool.Add(ReadItemFromDatabase()); 

(Lưu ý rằng hồ bơi không phải là thread-safe; sử dụng bình thường sẽ không cần đến nó để được.)

Bằng cách này bạn có thể vứt bỏ hồ bơi của bạn ngay sau khi bạn không còn cần nó nữa, thay vì có một số lượng lớn các chuỗi trong bộ nhớ mãi mãi. Bạn cũng có thể làm cho nó thông minh hơn, triển khai bộ nhớ cache LRU hoặc một cái gì đó nếu bạn thực sự muốn.

EDIT: Chỉ cần làm rõ lý do tại sao điều này tốt hơn sử dụng String.Intern ... giả sử bạn đọc một chuỗi các chuỗi từ cơ sở dữ liệu hoặc tệp nhật ký, xử lý chúng và sau đó chuyển sang tác vụ khác. Nếu bạn gọi String.Intern trên các chuỗi đó, chúng sẽ không bao giờ là rác được thu thập miễn là AppDomain của bạn vẫn còn hoạt động - và thậm chí có thể là không. Nếu bạn tải một số tệp nhật ký khác nhau, bạn sẽ dần dần tích luỹ chuỗi trong hồ bơi thực tập của mình cho đến khi bạn hoàn thành hoặc hết bộ nhớ. Thay vào đó, tôi đề xuất một mẫu như sau:

void ProcessLogFile(string file) 
{ 
    StringPool pool = new StringPool(); 
    // Process the log file using strings in the pool 
} // The pool can now be garbage collected 

Ở đây bạn có được lợi ích của nhiều chuỗi trong cùng một tệp chỉ tồn tại một lần trong bộ nhớ (hoặc ít nhất, chỉ nhận được quá khứ0 một lần) nhưng bạn không ' t gây ô nhiễm tài nguyên "toàn cầu" (nhóm thực tập).

+0

Jon, bạn có thể giải thích về những gì bạn đạt được bằng cách làm điều này không? Tôi giả sử rằng bây giờ bạn sẽ có một hàm chuỗi so sánh có hiệu suất cao hơn cho các chuỗi trong nhóm? Hay tôi đang thiếu điểm ở đây? –

+0

Chỉnh sửa câu trả lời để giải thích ... –

+1

oh, vậy các chuỗi nội bộ tồn tại mãi mãi? Điều đó không tốt lắm, hehe. Cảm ơn vì đã chú ý điều đó. – Svish

6

Điều này ít nhiều đúng. Nó được gọi là "chuỗi interning". Chuỗi literals sẽ có mặt trong bộ nhớ chỉ một lần và mọi biến được đặt thành cùng một điểm giá trị cho biểu diễn đơn lẻ này. Các chuỗi được tạo ra trong mã không tự động được tập trung.

http://msmvps.com/blogs/manoj/archive/2004/01/09/1549.aspx

+0

được tạo bằng mã? không phải tất cả các chuỗi được tạo ra trong mã? hoặc bạn có nghĩa là các chuỗi mã hóa cứng, trái ngược với ... tức là các chuỗi được lấy từ một thời gian chạy cơ sở dữ liệu? – Svish

+0

Các chuỗi được tạo trong mã không được tự động interned, nhưng chúng có thể được thực hiện bằng cách sử dụng String.Intern(). Lưu ý rằng có một số khác biệt (lỗi?) Trong cách chuỗi rỗng được xử lý để thực hiện trong các phiên bản khác nhau của .NET: http://msdn.microsoft.com/en-us/library/system.string.intern.aspx?ppud = 4 –

+0

Vì vậy, khi tìm nạp chuỗi từ một cơ sở dữ liệu, tôi sẽ phải sử dụng String.Intern cho nó là trường hợp? – Svish

0

Để thực hiện chuỗi để "chia sẻ" bộ nhớ vị trí của họ là thực tập sinh họ trong hồ bơi sinh nội trú, trong đó có chứa một tham chiếu duy nhất để mỗi chuỗi chữ độc đáo tuyên bố hoặc tạo lập trình trong chương trình của bạn.

Lưu ý rằng tất cả các chuỗi ký tự trong mã đều được tự động interned.

1

Nếu tôi nhớ chính xác, chuỗi được mã hóa cứng trong mã được gộp riêng. Đây gọi là "thực tập nội trú" và có một phương pháp để truy vấn xem một chuỗi là: String.IsInterned Method

Trên trang đó dưới "Ghi chú" bạn có thể đọc:

Bộ thực thi ngôn ngữ chung tự động duy trì một bảng, gọi "intern pool", chứa một cá thể duy nhất của mỗi hằng số chuỗi duy nhất được khai báo trong một chương trình, cũng như bất kỳ cá thể duy nhất nào của Chuỗi bạn thêm theo chương trình.

Hy vọng điều này sẽ giúp bạn một chút và sửa tôi nếu tôi sai.

Matthias

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