Khái niệm: String Interning
.NET duy trì một **bảng intern pool** (bảng chứa chuỗi duy nhất) cho mỗi assembly/app domain. Các chuỗi literal (hard-coded trong code) tự động được intern. Phương thức string.Intern(string str) lấy tham chiếu đến chuỗi trong pool: nếu chuỗi đã có -> trả về tham chiếu đó; nếu chưa -> thêm vào pool và trả về tham chiếu mới.
Ví dụ 1: Intern chuỗi literal
using System;
public class Demo
{
public static void Main(string[] args)
{
string str1 = "David";
string str2 = string.Intern(str1);
Console.WriteLine($"String1: {str1}");
Console.WriteLine($"Interned reference: {str2}");
Console.WriteLine($"Same reference? {object.ReferenceEquals(str1, str2)}"); // True (literal đã intern)
}
}
Kết quả
String1: David
Interned reference: David
Same reference? True
Ví dụ 2: Intern chuỗi tạo tại runtime
using System;
public class Demo
{
public static void Main(string[] args)
{
string str1 = "50";
string str2 = "100";
Console.WriteLine($"String1: {str1}");
Console.WriteLine($"String2: {str2}");
// Intern str1 -> thêm vào pool nếu chưa có
string interned = string.Intern(str1);
Console.WriteLine($"Interned reference of str1: {interned}");
Console.WriteLine($"Same as str1? {object.ReferenceEquals(str1, interned)}"); // True
// Tạo chuỗi mới tại runtime (không phải literal)
string runtimeStr = new string(new char[] { '5', '0' });
Console.WriteLine($"\nRuntime string: {runtimeStr}");
Console.WriteLine($"Same reference as str1? {object.ReferenceEquals(str1, runtimeStr)}"); // False
// Intern runtime string
string internedRuntime = string.Intern(runtimeStr);
Console.WriteLine($"Interned runtime: {internedRuntime}");
Console.WriteLine($"Same as str1 (literal)? {object.ReferenceEquals(str1, internedRuntime)}"); // True!
}
}
Kết quả
String1: 50
String2: 100
Interned reference of str1: 50
Same as str1? True
Runtime string: 50
Same reference as str1? False
Interned runtime: 50
Same as str1 (literal)? True
Phương thức liên quan: string.IsInterned(string)
Kiểm tra chuỗi có trong pool **không thêm mới**. Trả về tham chiếu nếu có, null nếu không.
string s = "hello";
string interned = string.IsInterned(s); // "hello" (literal đã intern)
string runtime = new string(new char[] { 'h', 'e', 'l', 'l', 'o' });
string notInterned = string.IsInterned(runtime); // null (chưa intern)
string.Intern(runtime); // Thêm vào pool
string nowInterned = string.IsInterned(runtime); // "hello"
Khi nào dùng Intern?
- Tiết kiệm bộ nhớ khi có **nhiều chuỗi trùng lặp** sống lâu (ví dụ: config keys, enum names, log categories).
- So sánh tham chiếu (ReferenceEquals) nhanh hơn so sánh giá trị (Equals) -> dùng cho lookup cực nhanh.
- Deserialization lớn: intern các chuỗi lặp lại.
Cảnh báo
- Intern pool **không bị GC** cho đến khi AppDomain unload (hoặc process exit). Lạm dụng -> memory leak.
- Chỉ intern chuỗi **bất biến**, sống lâu, trùng lặp nhiều.
- Không intern input người dùng, dữ liệu ngẫu nhiên, chuỗi lớn.
So sánh hiệu năng
// So sánh 1 triệu lần
string a = "key"; string b = "key";
// Equals: ~50ms
// ReferenceEquals (sau Intern): ~5ms