- vừa được xem lúc

Cách để tối ưu bộ nhớ và giảm tải cho Garbage collector trong Golang

0 0 1

Người đăng: Hoang Le

Theo Viblo Asia

Stack và Heap trong Golang

Cũng như các ngôn ngữ khác, Golang phân loại các dữ liệu được phân loại thành 2 bộ nhớ lưu trữ chính đó là stack và heap Nhìn chung, stack chứa dữ liệu có khích thước và vòng đời mà Go complier có thể dự đoán được. Stack gồm các local function variables (biến local của hàm), function arguments(tham số đầu vào của hàm), return values

Stack được quản lý tự động và tuân thủ theo nguyên tắc LIFO (Last in first out). Khi một hàm được gọi, tất cả dữ liệu liên quan đến nó được đặt lên trên cùng cảu stack và khi hàm hoàn thành thì dữ liệu này cũng sẽ bị xóa đi. Stack hoạt động hiệu quả, giảm thiểu tối đa chi phí quản lý memory, và việc truy xuất dữ liệu cũng rất nhanh.

Dù có nhiều lợi ích như vậy nhưng không phải tất cả data của chương trình đều có thể nằm trong stack. với dữ liệu có thể thay đổi trong quá trình thực thi hoặc yêu cầu truy cập vượt qua phạm vi của hàm thì không thể được đưa vào stack vì trình biên dịch không thể dự đoán được cách sử dụng dữ liệu đó. Và những dữ liệu đó được lưu trữ trong heap.

Ngược lại với stack, việc truy xuất dữ liệu từ heap và quản lý nó là những quá trình tốn nhiều tài nguyên hơn

Phân bổ Stack vs Heap

Như đã nói ở trên thì stack phục vụ cho các values có kích thước và vòng đời có thể dự đoán được. Ví dụ về những giá trị như vậy bao gồm các biến trong 1 hàm,...

Trình biên dịch của Go sử dụng nhiều kỹ thuật khác nhau để quyết định nên cấp phát dữ liệu trên stack hay heap. Ví dụ csac slice được cấp phát sẵn có kích thước tối đa 64kb sẽ được gán vào stack, trong khi những slice vượt quá 64kb sẽ được dduwan vào heap. Tiêu chí tương tự cũng áp dụng cho array, những array có kích thước vượt quá 10MB sẽ được cấp phát trên heap.

Để xác định nơi cáp phát bộ nhớ của 1 biến cụ thể, bạn có thể sử dụng phâp tích escape (escape analysis). Để thực hiện việc này, chúng ta có thể biên dịch chương trình từ dòng lệnh build với cờ -gcflags=-m

go build -gcflags=-m main.go

và ví dụ mình có 1 code với arrayBefore10Mb và arrayAfter10Mb thì khi mình dùng escape analysis

package main func main() { var arrayBefore10Mb [1]int arrayBefore10Mb[0] = 1 var arrayAfter10Mb [1310721]int arrayAfter10Mb[0] = 1 sliceBefore64 := make([]int, 8192) sliceOver64 := make([]int, 8193) sliceOver64[0] = sliceBefore64[0]
}

Sẽ được kết quả là

Golang/main.go:3:6: can inline main
Golang/main.go:7:6: moved to heap: arrayAfter10Mb
Golang/main.go:10:23: make([]int, 8192) does not escape
Golang/main.go:11:21: make([]int, 8193) escapes to heap

arrayAfter10Mb đã move lên heap thay vì stack

Garbage Collector: Managing the Heap

Một cách tiếp cận hiệu quả để xử lý heap là tránh sử dụng nó. Tuy nhiên, nếu dữ liệu đã bị cấp phát lên heap, thì chúng ta có thể làm gì?

Trái ngược với stack, heap có kích thước không giới hạn và có thể phát triển liên tục. Heap là nơi lưu trữ các đối tượng được tạo ra một cách động như struct, slice, map, và các khối bộ nhớ lớn không thể chứa trong giới hạn của stack.

Bộ gom rác (garbage collector) là công cụ duy nhất có khả năng thu hồi bộ nhớ từ heap và ngăn việc heap bị đầy hoàn toàn.

Hiểu về Garbage Collector

Bộ gom rác (garbage collector - GC) là một hệ thống chuyên biệt được thiết kế để xác định và giải phóng bộ nhớ được cấp phát động.

Go sử dụng thuật toán gom rác dựa trên kỹ thuật truy vết (tracing), cụ thể là mô hình Mark and Sweep. Trong giai đoạn đánh dấu (marking phase), GC sẽ xác định và đánh dấu những vùng dữ liệu vẫn đang được ứng dụng sử dụng là “live” (live heap). Sau đó, trong giai đoạn quét (sweeping phase), GC sẽ duyệt qua các vùng bộ nhớ không được đánh dấu và giải phóng chúng để tái sử dụng.

Tuy nhiên, hoạt động của garbage collector có chi phí và tiêu tốn hai tài nguyên hệ thống quan trọng: thời gian CPU và bộ nhớ vật lý.

Bộ nhớ liên quan đến GC bao gồm:

  • Live heap memory – bộ nhớ đã được đánh dấu là "live" trong chu kỳ GC trước đó.

  • New heap memory – bộ nhớ heap mới được cấp phát nhưng chưa được GC xử lý.

  • Metadata – thông tin quản lý, thường nhỏ hơn nhiều so với hai loại bộ nhớ trên.

Việc GC tiêu thụ thời gian CPU phụ thuộc vào cách nó được thiết kế. Một số bộ gom rác theo mô hình "stop-the-world" sẽ tạm dừng hoàn toàn việc thực thi chương trình trong suốt quá trình thu gom, dẫn đến việc lãng phí CPU cho các tác vụ không mang lại giá trị trực tiếp.

Trong Go, garbage collector không hoàn toàn là "stop-the-world". Phần lớn quá trình, bao gồm đánh dấu heap, được thực hiện song song với việc chạy ứng dụng. Tuy vậy, vẫn có một số giới hạn nhất định, và GC vẫn sẽ tạm dừng ngắn các đoạn mã đang thực thi trong một số giai đoạn nhất định.

Bạn có thể sử dụng trace tool của go để có thể quan sát sự thay đổi về kishc thước heap và phân tích hành vi của GC.

Giảm tải cho GC

Giảm số lượng allocation

  • Hạn chế tạo object trên heap:
  • Tạo object nhỏ gọn, nằm trên stack (giúp go compiler có thể escape analysis)
  • Dùng value type khi truyền qua các hàm nếu không cần sửa dữ liệu
// Good
type User struct { ID int Name string
} func handle(u User) { print(u) return
} // value pass // Bad
func handle(u *User) { print(u) return
}
  • Tránh tạo slice/map mới bên trong vòng lặp
// Bad
for i := 0; i < 1000; i++ { data := make([]byte, 100) process(data)
} // Good
buf := make([]byte, 100)
for i := 0; i < 1000; i++ { process(buf)
}

Giảm giữ tham chiếu nếu không cần thiết

var cache []string func do() { data := loadBigData() // sau khi xài xong data = nil // giải phóng tham chiếu sớm
}

Tối ưu cấu trúc dữ liệu

  • Tránh dùng map/slice quá lớn nếu không cần thiết.
  • Dùng []byte thay vì string nếu bạn thao tác nhị phân (vì string là immutable và có thể gây nhiều alloc).
  • Nếu slice dùng tạm thì nên gán slice = slice[:0] để tái sử dụng thay vì tạo slice mới trong vòng lặp

Chỉ tạo dữ liệu khi dùng

Có 1 số bạn có thói quyen tạo các biến ngay đầu function/method việc này cũng ảnh hưởng đến GC Thay vào đó khi nào sử dụng mới tạo.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Kotlin Extension Function ! Điểm chấm phá của Kotlin so với Java

Xin chào các bạn , mình là 1 moblie developer , lần đầu viết bài có gì sai sót mong các bạn thông cảm nhé !!. Khi các bạn cần các bạn phải viết đi viết lại nhiều lần rất tốn time, nhìn code thì rối.

0 0 32

- vừa được xem lúc

Kotlin Colletion và Sequences sự khác biệt là gì ? (Phần 1)

**Xin chào các bạn, hôm nay mình xin chia sẻ các bạn về collections và sequences trong kotlin . **.

0 0 49

- vừa được xem lúc

SOLID Principles

What is SOLID principles. Let's have a look at each principle one by one. SOLID is the acronym of:. .

0 0 38

- vừa được xem lúc

Làm lập trình viên tại các công ty nước ngoài có gì giống và khác các công ty tại Việt Nam?

Sau 1-2 năm làm việc tại Việt Nam, các lập trình viên thường có xu hướng ứng tuyển làm onsite/remote cho các công ty công nghệ nước ngoài. Một số quốc gia mà người Việt thường hay hướng tới như Úc, Si

0 0 32

- vừa được xem lúc

Hỗ trợ xây dựng web bằng Wordpress

WordPress là một hệ thống quản lý nội dung (CMS) miễn phí và mã nguồn mở được phát triển bằng ngôn ngữ lập trình PHP và sử dụng cơ sở dữ liệu MySQL. Nó được sử dụng rộng rãi như một nền tảng để xây dự

0 0 84

- vừa được xem lúc

HTML là gì? Kiến thức Tổng quan Về HTML

Tổng quam về HTML Tmarketing. .

0 0 29