Memory and Performance
Chúng ta sẽ khám phá Pointer, Garbage Collection, và Memory Leaks & Profiling với độ chi tiết cao hơn, kèm theo các ví dụ phức tạp và phân tích.
1. Pointer (&
và *
) - Hiểu sâu và ứng dụng thực tế
&
và *
) - Hiểu sâu và ứng dụng thực tếHiểu sâu về Pointer:
Pointer trong Go không chỉ là công cụ để "truyền tham chiếu", mà còn liên quan chặt chẽ đến escape analysis và cách Go quản lý bộ nhớ.
Escape Analysis: Trình biên dịch Go quyết định biến được cấp phát trên stack hay heap. Nếu một biến được truyền qua pointer và "thoát" khỏi phạm vi stack (ví dụ: trả về từ hàm), nó sẽ được cấp phát trên heap.
Ví dụ nâng cao:
Phân tích:
noEscape
: Truyền giá trị, copy toàn bộ struct (1000 int ≈ 8KB trên 64-bit). Không hiệu quả với dữ liệu lớn.withPointer
: Truyền pointer, chỉ 8 byte (địa chỉ). Hiệu quả hơn, nhưng vẫn trên stack nếu không thoát.escapePointer
: Trả về pointer, buộc cấp phát trên heap, ảnh hưởng đến GC.
Ứng dụng thực tế:
Mutex và Pointer: Trong lập trình đồng thời, pointer thường được dùng với
sync.Mutex
để bảo vệ dữ liệu.
Nil Pointer Panic: Pointer có thể là
nil
, cần kiểm tra cẩn thận:
Best Practices:
Chỉ dùng pointer khi cần thay đổi dữ liệu gốc hoặc tối ưu hiệu suất.
Tránh lạm dụng pointer với dữ liệu nhỏ (int, bool), vì copy thường rẻ hơn quản lý pointer.
Hiểu escape analysis qua lệnh
go build -gcflags="-m"
.
2. Garbage Collection (GC) - Cơ chế nội tại và tối ưu hóa
Cơ chế chi tiết:
Go dùng Tri-color Mark-and-Sweep GC:
Black: Đã được đánh dấu, vẫn đang sử dụng.
Grey: Đang chờ kiểm tra tham chiếu.
White: Không được tham chiếu, sẽ bị thu hồi.
Write Barrier: Đảm bảo tính chính xác khi GC chạy đồng thời với chương trình (concurrent).
Pacing Algorithm: Từ Go 1.5, GC tự điều chỉnh tần suất dựa trên
GOGC
và tải ứng dụng.
Tối ưu hóa GC:
Giảm áp lực GC:
Tránh cấp phát heap không cần thiết (dùng stack khi có thể).
Tái sử dụng bộ nhớ với
sync.Pool
cho các đối tượng tạm thời.
Đo lường hiệu suất GC:
Ứng dụng thực tế:
sync.Pool để giảm cấp phát:
Giảm cấp phát lặp lại, đặc biệt trong server xử lý request lớn.
Tuning GOGC:
High Throughput:
GOGC=off
(tắt GC, dùng thủ công) hoặcGOGC=1000
.Low Latency:
GOGC=10
, GC chạy thường xuyên hơn.
Debug GC:
Dùng
runtime/debug
để ghi log:
3. Memory Leaks & Profiling (pprof
) - Phân tích chuyên sâu
pprof
) - Phân tích chuyên sâuNguyên nhân Memory Leak nâng cao:
Goroutine Leak: Goroutine không thoát do channel bị chặn hoặc context không hủy.
Slice/Map Growth: Slice giữ tham chiếu cũ khi cắt (do capacity không giảm).
Finalizer Misuse: Dùng
runtime.SetFinalizer
không đúng cách.
Ví dụ Goroutine Leak:
Sửa bằng Context:
Profiling với pprof
:
Heap Profile:
Chạy:
go tool pprof http://localhost:6060/debug/pprof/heap
.Dùng
list <function>
để xem chi tiết cấp phát trong hàm.
Goroutine Profile:
Chạy:
go tool pprof http://localhost:6060/debug/pprof/goroutine
.Xác định goroutine bị treo qua stack trace.
Trace:
Chạy:
curl -o trace.out http://localhost:6060/debug/pprof/trace?seconds=5
.Mở:
go tool trace trace.out
để xem timeline.
Ví dụ thực tế với pprof
:
Chạy chương trình, sau đó dùng
go tool pprof http://localhost:6060/debug/pprof/heap
.Kết quả sẽ chỉ ra
expensive
cấp phát quá nhiều bộ nhớ.
Best Practices:
Dùng
context
để kiểm soát goroutine.Kiểm tra capacity của slice khi cắt:
s = s[:0:0]
để reset.Tích hợp
pprof
vào CI/CD để phát hiện leak sớm.
Kết luận
Pointer: Hiểu escape analysis và tối ưu cấp phát.
GC: Tối ưu qua
sync.Pool
và điều chỉnhGOGC
.Profiling: Dùng
pprof
để debug chuyên sâu, kết hợp context để tránh leak.
Last updated