2014-07-21 3 views
34

golang 프로그램을 작성했습니다. 런타임시 1.2GB의 비용이 듭니다. 내가 덤프를 가지고하는골란 메모리를 분석하는 방법

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

를 사용할 때 는.

이 힙 만 323.4MB을 보여줍니다. 다른 메모리는 어떻게 되었습니까?

golang 런타임 메모리를 설명하는 데 유용한 도구가 있습니까? 내가 gcvis를 사용할 때


내가이

enter image description here

와 힙 양식 프로파일을 가지고 enter image description here

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

포스트 코드를 참조하십시오. 귀하의 프로그램이하는 것을 저희에게 알려주십시오. – elithrar

+0

GC 때문에 어쩌면? http://dave.cheney.net/2014/07/11/visualising-the-go-garbage-collector가 도움이 될 수 있습니다. – VonC

+0

remaning memory가 garbaged로 수집되지 않고 시스템에 릴리스 된 것처럼 보입니다. 몇 분 동안 사용하지 않으면 완료됩니다. 8 분을 기다렸다가 다시 확인하십시오. Go 프로그램을 디버깅/프로파일 링하는 방법에 대한 안내는이 링크에서 확인하십시오. https://software.intel.com/en-us/blogs/2014/05/10/debugging-performance-issues-in-go-programs – siritinga

답변

33

힙 프로필에 내 코드는 활성 메모리를 보여줍니다 , 기억 t 그는 런타임이 가비지 컬렉터에 의해 수집되지 않았 음을 의미합니다. GC가 메모리를 수집 할 때 프로필은 축소되지만 메모리는 시스템에 반환되지 않습니다. 장래의 할당은 시스템에 더 많은 것을 요구하기 전에 이전에 수집 된 오브젝트 풀에서 메모리를 사용하려고합니다.

외부에서 보면 프로그램의 메모리 사용량이 증가하거나 계속 유지된다는 의미입니다. 외부 시스템이 프로그램의 "Resident Size"로 표시하는 것은 사용중인 go 값이나 수집 된 값을 보유하고 있는지 여부에 관계없이 프로그램에 할당 된 RAM의 바이트 수입니다. 때문에

이 두 숫자는 종종 매우 다른 이유는 다음과 같습니다

  1. GC의 수집 메모리가 프로그램의 외부보기에 아무런 영향을 미치지 않습니다
  2. 메모리 단편화는
  3. GC의는 경우에만 실행 사용중인 메모리가 이전 GC 이후의 메모리를 두 배로 만듭니다 (기본값 : http://golang.org/pkg/runtime/#pkg-overview 참조)

이동 방법에 대한 정확한 분석을 원한다면 당신이 메모리를 사용할 수 s를 runtime.ReadMemStats 호출 : http://golang.org/pkg/runtime/#ReadMemStats

을 양자 택일로, 당신은 귀하의 브라우저를 통해 프로파일 데이터에 액세스 할 수있는 경우에 당신이 웹 기반의 프로파일 링을 사용하고 있기 때문에 : http://10.10.58.118:8601/debug/pprof/ 힙 링크를 클릭하면 디버깅을 보여줍니다 런타임에 런타임 .MemStats 구조의 인쇄물이있는 힙 프로파일의보기.

런타임.MemStats 문서 (http://golang.org/pkg/runtime/#MemStats는) 모든 필드의 설명을 가지고 있지만,이 논의에 대한 흥미로운 사람은 다음과 같습니다

  • 를 HeapAlloc : 기본적으로 어떤 프로파일 러 (활성 힙 메모리) 당신에게
  • ALLOC주고있다 :를 HeapAlloc과 유사한를 하지만, 모든 이동 관리되는 메모리
  • sys 인에 대한 : 메모리 (주소 공간)의 총량 OS에서 요청

이동이 요구하시는 것 때문에 여전히 sys 인 사이의 불일치, 그리고 무엇 OS의보고가 될 것입니다 시스템, 운영체제 g 항상 동일하지는 않습니다. 또한 CGO/syscall (예 : malloc/mmap) 메모리는 이동에 의해 추적되지 않습니다.

+0

1.3.3 및 웹 기반 프로파일 링을 사용하고 있지만'/ debug/pprof/heap'에는 runtime.MemStats struct – IanB

+6

"시스템에 메모리가 반환되지 않았습니다"라는 메시지가 완전히 정확하지 않습니다. godoc 런타임/디버그 #FreeOSMemory()를 참조하십시오. – jimx

+1

이것은 과거에는 다를 수 있었지만 [runtime.MemStats] (https://golang.org/pkg/runtime/#MemStats)의 현재 문서에 따르면 'Alloc'과'HeapAlloc '은 같은 의미입니다 . – julen

18

@ Cookie of Nine의 답변에 덧붙여 간단히 말해서 --alloc_space 옵션을 사용해보십시오.

go tool pprof 기본적으로 --inuse_space을 사용하십시오. 결과가 실제 값의 서브 세트가되도록 메모리 사용을 샘플링합니다.
--alloc_space pprof는 프로그램 시작 이후 할당 된 메모리를 모두 반환합니다.

+1

'--alloc_space'는 내가 찾고있는 것입니다. –

2

StackImpact도 사용할 수 있습니다. StackImpact은 일반 및 예외 트리거 메모리 할당 프로필을 대시 보드에 자동으로 기록하고보고합니다.이 프로필은 기록 및 유사한 형태로 제공됩니다. 나는 항상 내 이동 애플리케이션의 성장 주거 메모리에 대한 혼란스러워했다 StackImpact

+0

StackImpact를 시도하고 메모리 누수가 엄청나게 증가했습니다. 메모리 누수 지점 중 하나 https://pastebin.com/ZAPCeGmp – Vlad

+0

메모리 누설 감지에 적합하지 않은'--alloc_space'를 사용하고있는 것처럼 보입니다. 프로그램 시작 후 할당 된 메모리 양만 표시됩니다. 장기 실행 프로그램의 경우 숫자가 상당히 높아질 수 있습니다. 지금까지 StackImpact 에이전트에서 메모리 누출을 인식하지 못했습니다. – logix

4

작동, 그리고 마지막으로 나는 프로파일 링 도구를 배워야했다 : 자세한 내용은 Memory Leak Detection in Production Go Applications

enter image description here

면책 조항이 블로그 게시물을 참조 Go 생태계에 존재합니다. 런타임은 runtime.Memstats 구조 내에서 많은 메트릭을 제공하지만 메모리 증가 원인을 찾는 데 도움이 될 수있는 요소가 무엇인지 이해하기 어려울 수 있으므로 몇 가지 추가 도구가 필요합니다. 응용 프로그램에서 환경

사용 https://github.com/tevjef/go-runtime-metrics 프로파일

. 예를 들어, 당신은 당신의 main이 넣을 수 있습니다 :

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 
    } 
} 

실행 InfluxDBGrafanaDocker 용기 내 :

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 

GrafanaInfluxDBGrafana (Grafana 메인 페이지 사이의 상호 작용을 설정 -> 왼쪽 상단 -> 데이터 소스 -> 새 데이터 소스 추가) :

enter image description here

가져 오기 대시 보드 #3242https://grafana.com에서 (Grafana 메인 페이지 -> 왼쪽 상단 -> 대시 보드 -> 가져 오기) :

마지막으로

enter image description here

, 응용 프로그램을 실행합니다 : 그것은 contenerized Influxdb에 런타임 통계를 전송합니다.합리적인 부하하에 응용 프로그램을 넣으십시오 (제 경우에는 몇 시간 동안 5 RPS가 상당히 작았습니다).

메모리 사용 분석

  1. Sys 곡선 (RSS의 synonim)이 HeapSys 곡선과 매우 유사하다. 동적 메모리 할당이 전반적인 메모리 증가의 주요 요인 이었으므로 스택 변수에 의해 소비되는 소량의 메모리는 일정한 것으로 보이고 무시 될 수 있습니다.
  2. goroutine의 일정한 양은 goroutine 누수/스택 변수 누락을 보장합니다.
  3. 할당 된 개체의 총 크기는 프로세스 수명 동안 동일하게 유지됩니다 (변동을 고려하지 않음).
  4. 가장 놀라운 사실 : HeapIdleSys과 동일한 비율로 증가하고있는 반면 HeapReleased은 항상 0입니다. 사람들을 위해
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

문제를 조사하려고 : 물론 런타임은 적어도이 테스트의 조건에서, 모든에서 OS 에 메모리를 반환하지 않습니다 메모리 소비량 나는 goroutine 누출과 같은 사소한 오류를 제외하기 위해 설명 된 단계를 따르는 것이 좋습니다.

확보 메모리는 명시 적으로

사람이 크게 debug.FreeOSMemory()에 명시 적으로 호출과 메모리 사용량을 줄일 수 있다는 그것은 흥미로운 :

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

comparison

사실,이 방법은 약 35 %를 저장 기본 조건과 비교하여 메모리가 부족합니다.

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