99 Ngày Java — Ngày 77: String Pool và Interning
Hai chuỗi có cùng ký tự có thể là cùng một đối tượng, hoặc hai đối tượng khác nhau. String pool là cơ chế chia sẻ bản chuẩn của chuỗi; intern() cho phép yêu cầu bản chuẩn ấy. Hiểu nó giúp đọc code đúng và tránh tối ưu bộ nhớ bằng trực giác sai.

1. Literal được chia sẻ, new tạo đối tượng
Các literal cùng nội dung được intern, nên hai biến gán cùng literal thường trỏ một đối tượng. new String("java") yêu cầu một đối tượng String khác. Dùng equals() để so nội dung; == chỉ so danh tính. Đừng xây logic dựa trên kết quả == của chuỗi, dù ví dụ literal có vẻ chạy đúng.
String a = "java";
String b = "java";
String c = new String("java");
System.out.println(a == b); // true
System.out.println(a == c); // false
System.out.println(a.equals(c)); // true2. intern() trả bản chuẩn
String.intern() trả về bản chuẩn trong pool: nếu có chuỗi bằng nội dung thì nhận bản đó; nếu chưa có, pool ghi nhận một bản. Nó không đổi đối tượng mà biến đang giữ. Với chuỗi ghép từ dữ liệu đầu vào, intern() có thể giảm bản sao khi tập giá trị nhỏ và lặp lại nhiều, nhưng cũng làm pool và GC chịu thêm việc.
String dynamic = new StringBuilder().append("ja").append("va").toString();
String canonical = dynamic.intern();
System.out.println(canonical == "java"); // true
System.out.println(dynamic.equals(canonical)); // true3. Nối chuỗi và constant folding
Biểu thức chỉ gồm hằng chuỗi có thể được compiler gộp ngay khi biên dịch. Biểu thức có biến được tính lúc chạy; cách JVM thực hiện có thể thay đổi. Vì vậy, đừng học thuộc rằng mọi phép + đều tạo đúng một số đối tượng cố định. Dùng javap -c để xem bytecode, JFR để đo cấp phát dưới tải thực.
String folded = "ja" + "va"; // compile-time constant
String part = "ja";
String runtime = part + "va";
System.out.println(folded == "java"); // true
System.out.println(runtime.equals("java")); // true4. Khi nào nên chia sẻ chuỗi
Chỉ cân nhắc intern cho từ vựng hữu hạn và lặp nhiều, sau khi heap histogram cho thấy bản sao String thực sự tốn bộ nhớ. Với ID người dùng hoặc request không giới hạn, pool không phải cache có TTL. Nếu cần cache, dùng cấu trúc có giới hạn, đo hit rate và eviction. G1 còn có string deduplication riêng; nó khác với việc ứng dụng gọi intern().
So nội dung bằng equals(), không bằng ==.
Đo bản sao trước khi tối ưu pool.
String pool giúp chia sẻ, nhưng không biến chuỗi thành dữ liệu bất tử hay cache miễn phí. Ngày 78 đọc bytecode bằng javap để thấy compiler thực sự đã tạo gì.
Tài liệu đối chiếu
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html
https://docs.oracle.com/en/java/javase/21/docs/specs/man/javap.html
Site Admin
Engineer and writer. Building things with TypeScript and distributed systems.
Bình luận (0)
Bạn cần đăng nhập bằng Google để bình luận.
Hãy là người bình luận đầu tiên.


