Bài 6: Tránh tạo object không cần thiết
Bad practice thường gặp (tạo object dư thừa)
boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
}
Vấn đề của cách này:
- Mỗi lần gọi
matcheslà mỗi lần compile regex mới. - Ở hot path, chi phí tạo object lặp lại gây tốn CPU và tăng GC pressure.
Nguyên tắc cốt lõi: ưu tiên tái sử dụng object hiện có thay vì tạo object mới khi không cần. Việc tạo quá nhiều object ngắn hạn có thể làm tăng GC pressure, giảm hiệu năng, và làm code rối hơn.
Các trường hợp nên tránh tạo object dư thừa
1. Tái sử dụng immutable object
Với immutable object, nếu giá trị không đổi, bạn có thể dùng lại an toàn.
Ví dụ không tốt:
String s = new String("hello");
Ví dụ tốt:
String s = "hello";
Literal string được intern và tái sử dụng tốt hơn trong đa số trường hợp.
2. Dùng static factory thay vì constructor khi phù hợp
Boolean.valueOf("true") thường tốt hơn new Boolean("true") (constructor đã lỗi thời) vì có thể trả về object dùng chung.
3. Tránh tạo object lặp lại trong vòng lặp
Đặc biệt với object đắt đỏ (regex Pattern, formatter, parser...).
Ví dụ tối ưu regex:
import java.util.regex.Pattern;
public class RomanNumerals {
private static final Pattern ROMAN =
Pattern.compile("^(?=.)M*(C[MD]|D?C{0,3})"
+ "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
public static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
Nếu compile regex mỗi lần gọi method, bạn sẽ trả chi phí lặp đi lặp lại không cần thiết.
4. Ưu tiên primitive khi phù hợp, tránh autoboxing không cần thiết
Autoboxing có thể tạo nhiều object wrapper (Long, Integer, ...) ngoài ý muốn.
Ví dụ kém hiệu năng:
Long sum = 0L;
for (long i = 0; i <= 1_000_000L; i++) {
sum += i;
}
Ví dụ tốt hơn:
long sum = 0L;
for (long i = 0; i <= 1_000_000L; i++) {
sum += i;
}
Lưu ý quan trọng
- Không phải mọi object allocation đều xấu. JVM hiện đại tối ưu allocation rất tốt.
- Mục tiêu là tránh allocation vô nghĩa ở điểm nóng (hot path), không phải tối ưu cực đoan mọi nơi.
- Đừng giữ cache quá tay cho object nhẹ vì có thể tăng memory footprint và độ phức tạp.
Cân bằng giữa hiệu năng và độ rõ ràng
- Viết code rõ ràng trước.
- Dùng profiler để xác định bottleneck thật.
- Chỉ tối ưu nơi có dữ liệu đo đạc chứng minh cần thiết.
Checklist áp dụng nhanh
- Có object nào đang được tạo lặp lại nhưng giá trị không đổi không?
- Có thể chuyển sang static final để tái sử dụng không?
- Có autoboxing trong loop lớn không?
- Có constructor nào nên thay bằng static factory không?
- Đã benchmark/profiler trước khi tối ưu chưa?
Tóm lại: tránh tạo object không cần thiết giúp code hiệu quả hơn, nhưng tối ưu cần dựa trên đo đạc và ngữ cảnh thực tế.
All rights reserved