欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Golang使用pprof檢查內(nèi)存泄漏的全過程

 更新時間:2024年02月04日 09:35:33   作者:hankeyyh  
pprof 是golang提供的一款分析工具,可以分析CPU,內(nèi)存的使用情況,本篇文章關(guān)注它在分析內(nèi)存泄漏方面的應(yīng)用,本文給大家介紹了Golang使用pprof檢查內(nèi)存泄漏的全過程,文中通過代碼給大家介紹的非常詳細,需要的朋友可以參考下

前言

pprof 是golang提供的一款分析工具,可以分析CPU,內(nèi)存的使用情況,本篇文章關(guān)注它在分析內(nèi)存泄漏方面的應(yīng)用。pprof 不能直觀顯示出某個函數(shù)發(fā)生了泄漏,它的用途是列出當前內(nèi)存分配較大的位置,通過比較一段時間pprof的結(jié)果,可以發(fā)現(xiàn)內(nèi)存變化。

測試代碼

比如我們有下面的函數(shù),在請求中對全局變量分配了一塊內(nèi)存,但沒有回收:

package main

import (
    "fmt"
    "log"
    "net/http"
    _ "net/http/pprof" // 關(guān)鍵??!
    "runtime"
    "sync"
)

type UserData struct {
    Data []byte
}

type UserCache struct {
    mu    sync.Mutex
    Cache map[string]*UserData
}

func (uc *UserCache) clear() {
    uc.mu.Lock()
    defer uc.mu.Unlock()
    uc.Cache = make(map[string]*UserData)
}

func NewUserCache() *UserCache {
    return &UserCache{
        Cache: make(map[string]*UserData),
    }
}

var userCache = NewUserCache()

// 分配全局內(nèi)存
func handleRequest(w http.ResponseWriter, r *http.Request) {
    userCache.mu.Lock()
    defer userCache.mu.Unlock()
    
    userData := &UserData{
        Data: make([]byte, 1000000),
    }

    userID := fmt.Sprintf("%d", len(userCache.Cache))
    // 賦值給全局變量,但沒有回收
    userCache.Cache[userID] = userData
    log.Printf("Added data for user %s. Total users: %d\n", userID, len(userCache.Cache))
}

// 清空全局內(nèi)存
func handleClear(w http.ResponseWriter, r *http.Request) {
    userCache.clear()
    runtime.GC()
}

func main() {
    http.HandleFunc("/leaky-endpoint", handleRequest)
    http.HandleFunc("/clear", handleClear)
    
    http.ListenAndServe(":8080", nil)
}

其中 import "net/http/pprof" 引入了pprof工具,啟動服務(wù)后,內(nèi)存分析結(jié)果保存在http://localhost:8080/debug/pprof/heap。

發(fā)送請求

我們往/leaky-endpoint發(fā)送一些請求,觸發(fā)全局內(nèi)存分配,可以使用 ab 命令:

ab -n 1000 -c 10 http://localhost:8080/leaky-endpoint

-n:請求數(shù)量,我們總共發(fā)送1000個請求

-c:并發(fā)數(shù)量,1000個請求通過10個并發(fā)線程發(fā)送

分析內(nèi)存

查看內(nèi)存分配

我們可以通過下面的命令查看分析結(jié)果:

pprof http://localhost:8080/debug/pprof/heap

// output: 
// Entering interactive mode (type "help" for commands, "o" for options)
// (pprof)

輸入上述命令后,會進入命令行交互模式,我們可以輸入一系列幫助命令查看內(nèi)存分析結(jié)果:

  • top:輸出內(nèi)存分配最多的幾個函數(shù)
(pprof) top
Showing nodes accounting for 487.41MB, 100% of 487.41MB total
      flat  flat%   sum%        cum   cum%
  487.41MB   100%   100%   487.41MB   100%  main.handleRequest
         0     0%   100%   487.41MB   100%  net/http.(*ServeMux).ServeHTTP
         0     0%   100%   487.41MB   100%  net/http.(*conn).serve
         0     0%   100%   487.41MB   100%  net/http.HandlerFunc.ServeHTTP
         0     0%   100%   487.41MB   100%  net/http.serverHandler.ServeHTTP

可以看到,handleRequest 分配了最多的內(nèi)存。這幾列的含義是:

  • flat:函數(shù)自身在采樣期間直接消耗的內(nèi)存,不包括它調(diào)用的其他函數(shù)的消耗;
  • flag%: flat 值占總消耗資源的百分比,這個百分比是基于整個程序在采樣期間的資源消耗來計算的;
  • sum%: 累積百分比,表示從輸出列表頂部開始到當前行為止的所有函數(shù)的 flat 百分比之和。這可以幫助你理解最頂部的函數(shù)累積起來消耗了多少資源;
  • cum: 函數(shù)自身flat,加上它調(diào)用的所有函數(shù)在采樣期間消耗的內(nèi)存;
  • cum%: cum 值占總消耗資源的百分比。這個百分比顯示了函數(shù)及其所有遞歸調(diào)用的資源消耗在程序總資源消耗中的比重;
  • list:列出函數(shù)具體分配內(nèi)存的位置
(pprof) list main.handleRequest
Total: 487.41MB
ROUTINE ======================== main.handleRequest in /home/yuhanyang/project/HelloGo/test_mem_leak/main.go
 487.41MB   487.41MB (flat, cum)   100% of Total
        .          .     35:func handleRequest(w http.ResponseWriter, r *http.Request) {
        .          .     36:   userCache.mu.Lock()
        .          .     37:   defer userCache.mu.Unlock()
        .          .     38:
        .          .     39:   userData := &UserData{
 487.41MB   487.41MB     40:           Data: make([]byte, 1000000),
        .          .     41:   }
  • web:在瀏覽器中生成并打開 SVG 格式的調(diào)用圖

  • png:生成 PNG 格式的調(diào)用圖

比較內(nèi)存分配

為了分析內(nèi)存泄漏,我們往往需要統(tǒng)計一段時間開始和結(jié)束時的內(nèi)存變化情況,在上面的基礎(chǔ)上,我們可以再用ab命令觸發(fā)幾次內(nèi)存分配,然后通過pprof查看:

>>pprof  http://localhost:8080/debug/pprof/heap
(pprof) top
Showing nodes accounting for 3.74GB, 100% of 3.74GB total
      flat  flat%   sum%        cum   cum%
    3.74GB   100%   100%     3.74GB   100%  main.handleRequest
         0     0%   100%     3.74GB   100%  net/http.(*ServeMux).ServeHTTP
         0     0%   100%     3.74GB   100%  net/http.(*conn).serve
         0     0%   100%     3.74GB   100%  net/http.HandlerFunc.ServeHTTP
         0     0%   100%     3.74GB   100%  net/http.serverHandler.ServeHTTP

發(fā)現(xiàn)內(nèi)存較之前增長了,實際上我們可以通過 -diff_base 來比較兩次采樣結(jié)果的差異:

// 第一次采樣
pprof http://localhost:8080/debug/pprof/heap > base.out

// 當前采樣
pprof http://localhost:8080/debug/pprof/heap > current.out

// 差異分析
pprof -diff_base=base.out current.out

查看啟動以來的內(nèi)存分配

上面的pprof實際統(tǒng)計的是采樣時刻的內(nèi)存分配情況,我們可以先清空全局內(nèi)存,然后再通過top查看,會發(fā)現(xiàn)不再有輸出了:

curl http://localhost:8080/clear
pprof  http://localhost:8080/debug/pprof/heap
(pprof) top
Showing nodes accounting for 0, 0% of 0 total
      flat  flat%   sum%        cum   cum%

但我們可以通過-sample_index=來控制采樣,對于內(nèi)存分析來說,它有下面幾種取值:

  • inuse_space:默認,表示程序在執(zhí)行時刻正在使用的內(nèi)存量;
  • inuse_objects:程序在執(zhí)行時刻正在使用的對象數(shù)量;
  • alloc_space:程序自啟動以來分配的總內(nèi)存量,不考慮這些內(nèi)存是否已經(jīng)被釋放;
  • alloc_objects:程序自啟動以來分配的總對象數(shù)量,不考慮這些對象是否已經(jīng)被釋放;

我們可以查看程序啟動以來的總內(nèi)存分配數(shù)據(jù):

pprof -sample_index=alloc_space http://localhost:8080/debug/pprof/heap
(pprof) top
Showing nodes accounting for 3842.05MB, 99.44% of 3863.75MB total
Dropped 44 nodes (cum <= 19.32MB)
Showing top 10 nodes out of 14
      flat  flat%   sum%        cum   cum%
 3832.36MB 99.19% 99.19%  3832.86MB 99.20%  main.handleRequest
    9.70MB  0.25% 99.44%    19.36MB   0.5%  compress/flate.NewWriter

以上就是Golang使用pprof檢查內(nèi)存泄漏的全過程的詳細內(nèi)容,更多關(guān)于Golang pprof內(nèi)存泄漏的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang雙指針快速排序的實現(xiàn)代碼

    golang雙指針快速排序的實現(xiàn)代碼

    這篇文章主要介紹了golang雙指針快速排序的實現(xiàn)代碼,通過實例代碼補充介紹了Golang實現(xiàn)快速排序和歸并排序以及堆排序算法全注釋,需要的朋友可以參考下
    2024-03-03
  • golang 連接mongoDB的方法示例

    golang 連接mongoDB的方法示例

    這篇文章主要介紹了golang 連接mongoDB的方法示例,詳細的介紹了golang的基礎(chǔ)知識和連接mongoDB的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法

    Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法

    本文主要介紹了Go?interface{}?轉(zhuǎn)切片類型的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • 深入了解Golang的指針用法

    深入了解Golang的指針用法

    與C語言一樣,Go語言中同樣有指針,通過指針,我們可以只傳遞變量的內(nèi)存地址,而不是傳遞整個變量,這在一定程度上可以節(jié)省內(nèi)存的占用。本文將通過示例詳細講講Golang的指針用法,需要的可以參考一下
    2022-07-07
  • 一篇文章讀懂Golang?init函數(shù)執(zhí)行順序

    一篇文章讀懂Golang?init函數(shù)執(zhí)行順序

    init()函數(shù)會在包被初始化后自動執(zhí)行,并且在main()函數(shù)之前執(zhí)行,但是需要注意的是init()以及main()函數(shù)都是無法被顯式調(diào)用的,下面這篇文章主要給大家介紹了關(guān)于如何通過一篇文章讀懂Golang?init函數(shù)執(zhí)行順序的相關(guān)資料,需要的朋友可以參考下
    2022-11-11
  • 基于Golang實現(xiàn)Redis分布式鎖解決秒殺問題

    基于Golang實現(xiàn)Redis分布式鎖解決秒殺問題

    這篇文章主要給大家介紹了使用Golang實現(xiàn)Redis分布式鎖解決秒殺問題,文中有詳細的代碼示例供大家參考,具有一定的參考價值,需要的朋友可以參考下
    2023-08-08
  • 一文帶你吃透Go語言中的原子操作

    一文帶你吃透Go語言中的原子操作

    原子操作是解決并發(fā)編程中共享數(shù)據(jù)訪問問題的一種常見機制,下面就來和大家深入介紹原子操作的原理、用法以及在解決并發(fā)問題中的應(yīng)用,需要的可以參考一下
    2023-06-06
  • 詳解Go中處理時間數(shù)據(jù)的方法

    詳解Go中處理時間數(shù)據(jù)的方法

    在許多場合,你將不得不編寫必須處理時間的代碼。在Go中處理時間數(shù)據(jù)需要你從Go標準庫中導(dǎo)入?time?包。這個包有很多方法和類型供你使用,但我選取了最常用的方法和類型,并在這篇文章中進行了描述,感興趣的可以了解一下
    2023-04-04
  • Go語言中validation庫不能校驗零值問題的解決方法

    Go語言中validation庫不能校驗零值問題的解決方法

    在使用 Gin 框架的時候,前后端傳遞數(shù)據(jù)的時候,比如使用 JSON 格式,通常會使用 ShouldBindJSON 去用結(jié)構(gòu)體打 tag 綁定前端傳來的 JSON 格式數(shù)據(jù),本文給大家介紹了Go語言中validation庫不能校驗零值問題的解決方法,需要的朋友可以參考下
    2024-08-08
  • Golang語言使用像JAVA?Spring注解一樣的DI和AOP依賴注入實例

    Golang語言使用像JAVA?Spring注解一樣的DI和AOP依賴注入實例

    這篇文章主要為大家介紹了Golang語言使用像JAVA?Spring注解一樣的DI和AOP依賴注入實例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10

最新評論