2014-07-21 16 views
34

Tôi đã viết một chương trình golang, Chi phí là 1,2 GB khi chạy. khi tôi sử dụngcách phân tích bộ nhớ golang

go tool pprof http://10.10.58.118:8601/debug/pprof/heap 

để có một bãi chứa.

Nó chỉ hiển thị 323.4MB trong heap. bộ nhớ khác là gì?

Có công cụ nào tốt hơn để giải thích bộ nhớ thời gian chạy golang không?


khi tôi sử dụng gcvis tôi đã

enter image description here

và hình thức đống hồ sơ này enter image description here

mã của tôi trên

https://github.com/sharewind/push-server/blob/v3/broker 
+1

bài viết mã của bạn. Cho chúng tôi biết chương trình của bạn làm gì. – elithrar

+0

Có thể vì một gc? http://dave.cheney.net/2014/07/11/visualising-the-go-garbage-collector có thể giúp ích. – VonC

+0

Dường như bộ nhớ remaning không được thu thập được thu thập và phát hành cho hệ thống. Nó được thực hiện sau một vài phút không hoạt động. Đợi 8 phút và kiểm tra lại. Kiểm tra liên kết này để biết hướng dẫn về cách gỡ lỗi/hồ sơ Đi chương trình: https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs – siritinga

Trả lời

33

Các đống hồ sơ cho thấy bộ nhớ tích cực , bộ nhớ t Thời gian chạy của ông tin tưởng đang được sử dụng bởi chương trình đi (ví dụ: chưa được thu thập bởi các nhà sưu tập rác). Khi GC thu thập bộ nhớ, cấu hình co lại, nhưng không có bộ nhớ nào được trả về hệ thống. Phân bổ trong tương lai của bạn sẽ cố gắng sử dụng bộ nhớ từ nhóm các đối tượng đã thu thập trước đó trước khi yêu cầu hệ thống hơn.

Từ bên ngoài, điều này có nghĩa là việc sử dụng bộ nhớ của chương trình của bạn sẽ tăng hoặc ở mức độ. Những gì hệ thống bên ngoài trình bày như là "Kích thước Resident" của chương trình của bạn là số byte RAM được gán cho chương trình của bạn cho dù nó đang giữ giá trị đi vào sử dụng hoặc thu thập.

Lý do khiến hai số này thường khá khác nhau là vì:

  1. Các GC nhớ thu thập không ảnh hưởng đến quan điểm bên ngoài của chương trình
  2. Memory phân mảnh
  3. Các GC chỉ chạy khi bộ nhớ đang sử dụng tăng gấp đôi bộ nhớ được sử dụng sau GC trước đó (theo mặc định, xem: http://golang.org/pkg/runtime/#pkg-overview)

Nếu bạn muốn phân tích chính xác cách xem s bộ nhớ bạn có thể sử dụng thời gian chạy.ReadMemStats gọi: http://golang.org/pkg/runtime/#ReadMemStats

Ngoài ra, vì bạn đang sử dụng hồ sơ dựa trên web nếu bạn có thể truy cập dữ liệu lược tả thông qua trình duyệt của mình tại: http://10.10.58.118:8601/debug/pprof/, nhấp vào liên kết heap sẽ hiển thị cho bạn gỡ lỗi xem hồ sơ heap, trong đó có một bản in của một cấu trúc runtime.MemStats ở phía dưới.

Thời gian chạy.MemStats tài liệu (http://golang.org/pkg/runtime/#MemStats) có giải thích về tất cả các lĩnh vực, nhưng những người thú vị cho cuộc thảo luận này là:

  • HeapAlloc: về cơ bản những gì các hồ sơ được đem lại cho bạn (hoạt động bộ nhớ heap)
  • Alloc: tương tự như HeapAlloc , nhưng đối với tất cả đi bộ nhớ được quản lý
  • Sys: tổng dung lượng bộ nhớ (không gian địa chỉ) yêu cầu từ hệ điều hành

vẫn sẽ là sự khác biệt giữa Sys, và những gì các báo cáo hệ điều hành vì những gì Go yêu cầu của và hệ điều hành gì ives nó không phải lúc nào cũng giống nhau. Ngoài ra CGO/syscall (ví dụ: malloc/mmap) bộ nhớ không được theo dõi bằng cách đi.

+0

Tôi đang sử dụng đi 1.3.3 và hồ sơ dựa trên web tuy nhiên '/ debug/pprof/heap' không chứa bản in của runtime.MemStats struct – IanB

+6

"Không có bộ nhớ nào được trả về hệ thống" hiện không hoàn toàn chính xác. Xem thời gian chạy/gỡ lỗi godoC#FreeOSMemory(). – jimx

+1

Điều này có thể khác trong quá khứ, nhưng theo các tài liệu hiện hành trên [runtime.MemStats] (https://golang.org/pkg/runtime/#MemStats), 'Alloc' và' HeapAlloc' có cùng ý nghĩa . – julen

18

Là một bổ sung cho câu trả lời của @Cookie of Nine, trong ngắn hạn: bạn có thể thử tùy chọn --alloc_space.

go tool pprof sử dụng --inuse_space theo mặc định. Nó lấy mẫu sử dụng bộ nhớ để kết quả là tập hợp con của thực tế.
Bằng cách --alloc_space pprof trả về tất cả bộ nhớ được phân bổ kể từ khi chương trình bắt đầu.

+1

'--alloc_space' chính xác là những gì tôi đang tìm kiếm. –

2

Bạn cũng có thể sử dụng StackImpact, tự động ghi lại và báo cáo cấu hình phân bổ bộ nhớ thường xuyên và bất thường được kích hoạt cho trang tổng quan, có sẵn ở dạng lịch sử và có thể so sánh. Xem bài viết trên blog này để biết thêm chi tiết Memory Leak Detection in Production Go Applications

enter image description here

Disclaimer: Tôi làm việc cho StackImpact

+0

Tôi đã thử StackImpact và rò rỉ bộ nhớ tăng lên rất nhiều. Một điểm rò rỉ bộ nhớ https://pastebin.com/ZAPCeGmp – Vlad

+0

Dường như bạn đang sử dụng '--alloc_space', không phù hợp để phát hiện rò rỉ bộ nhớ. Nó sẽ chỉ cho bạn thấy có bao nhiêu bộ nhớ được cấp phát kể từ khi chương trình bắt đầu. Đối với một chương trình chạy dài, các con số có thể khá cao. Chúng tôi không nhận thức được bất kỳ rò rỉ bộ nhớ nào trong tác nhân StackImpact cho đến nay. – logix

4

Tôi đã luôn luôn nhầm lẫn về ký ức dân cư ngày càng tăng của các ứng dụng Go tôi, và cuối cùng tôi đã phải học những công cụ profiling có mặt trong hệ sinh thái Go. Thời gian chạy cung cấp nhiều số liệu trong cấu trúc runtime.Memstats, nhưng có thể khó hiểu được cấu trúc nào trong số chúng có thể giúp tìm ra lý do phát triển bộ nhớ, do đó cần một số công cụ bổ sung.

Profiling môi trường

Sử dụng https://github.com/tevjef/go-runtime-metrics trong ứng dụng của bạn. Ví dụ, bạn có thể đặt điều này trong main của bạn:

import(
    metrics "github.com/tevjef/go-runtime-metrics" 
) 
func main() { 
    //... 
    metrics.DefaultConfig.CollectionInterval = time.Second 
    if err := metrics.RunCollector(metrics.DefaultConfig); err != nil { 
     // handle error 
    } 
} 

Run InfluxDBGrafana trong Docker container:

docker run --name influxdb -d -p 8086:8086 influxdb 
docker run -d -p 9090:3000/tcp --link influxdb --name=grafana grafana/grafana:4.1.0 

Thiết lập tương tác giữa GrafanaInfluxDBGrafana (trang chính Grafana -> góc trên cùng bên trái -> Dữ liệu -> Thêm nguồn dữ liệu mới):

enter image description here

nhập bảng điều khiển #3242 từ https://grafana.com (trang chính Grafana -> trái góc trên -> Dashboard -> Import):

enter image description here

Cuối cùng, khởi động ứng dụng của bạn: nó sẽ truyền số liệu thời gian chạy đến contenerized Influxdb.Đặt ứng dụng của bạn dưới một tải hợp lý (trong trường hợp của tôi nó là khá nhỏ - 5 RPS trong một vài giờ).

tiêu thụ bộ nhớ phân tích

  1. Sys (các synonim của RSS) đường cong khá giống với HeapSys đường cong. Hóa ra phân bổ bộ nhớ động là yếu tố chính của sự tăng trưởng bộ nhớ tổng thể, do đó, lượng bộ nhớ nhỏ được tiêu thụ bởi các biến stack dường như không đổi và có thể bỏ qua;
  2. Số tiền không đổi của goroutines garantees sự vắng mặt của rò rỉ goroutine/stack biến rò rỉ;
  3. Tổng số lượng đối tượng được phân bổ vẫn giữ nguyên (không có điểm trong việc tính đến các biến động) trong suốt thời gian tồn tại của quá trình.
  4. Thực tế đáng ngạc nhiên nhất: HeapIdle đang tăng với tỷ lệ giống như Sys, trong khi HeapReleased luôn bằng 0. Rõ ràng thời gian chạy không trả lại bộ nhớ để OS ở tất cả, ít nhất là theo các điều kiện của thử nghiệm này:
HeapIdle minus HeapReleased estimates the amount of memory  
that could be returned to the OS, but is being retained by 
the runtime so it can grow the heap without requesting more 
memory from the OS. 

enter image description hereenter image description here

Đối với những người đang cố gắng để điều tra vấn đề tiêu thụ bộ nhớ Tôi khuyên bạn nên làm theo các bước được mô tả để loại trừ một số lỗi nhỏ (như rò rỉ goroutine).

Giải phóng bộ nhớ một cách rõ ràng

Thật thú vị khi một trong những có thể làm giảm đáng kể mức tiêu thụ bộ nhớ với các cuộc gọi rõ ràng để debug.FreeOSMemory():

// in the top-level package 
func init() { 
    go func() { 
     t := time.Tick(time.Second) 
     for { 
      <-t 
      debug.FreeOSMemory() 
     } 
    }() 
} 

comparison

Trong thực tế, phương pháp này tiết kiệm khoảng 35% bộ nhớ so với các điều kiện mặc định.

Xem thêm

https://github.com/golang/go/issues/14521

Các vấn đề liên quan