Tại sao HashMap trong Java có thể bị biến thành Red-Black Tree từ Java 8?
Cuộc tấn công từ chối dịch vụ bằng xung đột mã băm Hash Collision DoS Trước Java 8, cấu trúc dữ liệu HashMap sử dụng một mảng các thùng Buckets, mỗi thùng là một danh sách liên kết đơn LinkedList để …
☰ Mục lục bài viết (4 phần) Mở / Đóng mục lục
Cuộc tấn công từ chối dịch vụ bằng xung đột mã băm (Hash Collision DoS)#
Trước Java 8, cấu trúc dữ liệu HashMap sử dụng một mảng các thùng (Buckets), mỗi thùng là một danh sách liên kết đơn (LinkedList) để giải quyết xung đột mã băm (Chaining method).
Độ phức tạp lý tưởng để tìm kiếm một phần tử là $O(1)$. Tuy nhiên, nếu một kẻ tấn công cố tình gửi hàng triệu request chứa các chuỗi ký tự khác nhau nhưng có cùng một giá trị hashCode(), tất cả các phần tử này sẽ rơi vào cùng một bucket duy nhất:
- Danh sách liên kết kéo dài hàng triệu node.
- Độ phức tạp tìm kiếm bị thoái hóa từ $O(1)$ thành $O(N)$!
- Thao tác
get()vàput()tiêu tốn 100% CPU, làm tê liệt toàn bộ ứng dụng (Hash Collision Denial-of-Service Attack).
Cải tiến đột phá trong Java 8: Treeifying Threshold#
Để hóa giải cuộc tấn công này, đội ngũ phát triển Java đã đưa ra một cơ chế chuyển đổi cấu trúc tự động:
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
Quy tắc hoạt động:
- Khi số lượng phần tử trong một bucket vượt quá ngưỡng
TREEIFY_THRESHOLD = 8và dung lượng mảng $\ge 64$:- Java sẽ tự động chuyển đổi danh sách liên kết đơn đó thành một Cây đỏ đen (Red-Black Tree - Cây nhị phân tìm kiếm tự cân bằng).
- Lúc này, độ phức tạp tìm kiếm trong trường hợp xấu nhất giảm ngoạn mục từ $O(N)$ xuống còn: $$O(\log N)$$
- Nếu các phần tử bị xóa bớt và số lượng giảm xuống dưới 6, cây sẽ tự động được chuyển đổi ngược lại thành LinkedList nhẹ nhàng để tiết kiệm bộ nhớ.
Vì sao lại chọn ngưỡng 8 mà không phải 2 hay 100?#
Theo phân phối xác suất Poisson: Với một hàm băm chuẩn (Uniform Hash Distribution), xác suất để 8 phần tử ngẫu nhiên rơi vào cùng một bucket là cực kỳ hiếm (nhỏ hơn 1 phần 10 triệu - $0.00000006$). Chỉ khi có sự cố bất thường hoặc tấn công có chủ đích thì ngưỡng 8 mới bị chạm tới!
Góc thảo luận#
Anh em đã từng phải viết lại phương thức equals() và hashCode() trong Java chưa? Quy tắc vàng khi override hai hàm này là gì?
Xem thêm nội dung liên quan:
Thảo luận
1 phản hồi
Tham gia thảo luận
Bài liên quan trong #Backend & Microservices
-
Áp dụng Model Context Protocol (MCP) của Anthropic để kết nối AI với Postgres và Docker
Hoàng Nam (Tech Lead) · 💬 1 -
So sánh mô hình Embedding đa ngữ: OpenAI text-embedding-3 vs BGE-M3 vs Cohere
Minh Trí (AI Engineer) · 💬 1 -
Xây dựng Voice AI Agent thời gian thực với WebRTC và Whisper Live: Độ trễ dưới 500ms
Lan Anh (Senior Frontend) · 💬 1 -
Cấu hình Rate Limiting ở tầng Nginx và API Gateway: Chống DDOS Layer 7 hiệu quả
Đức Huy (DevOps Specialist) · 💬 1 -
Tokenization tiếng Việt của các LLM: Tại sao chi phí tiếng Việt đắt gấp 3 lần tiếng Anh?
Minh Trí (AI Engineer) · 💬 1