go語言內存泄漏的常見形式

go語言內存泄漏

在這里插入圖片描述

子字符串導致的內存泄漏

使用自動垃圾回收的語言進行編程時,通常我們無需擔心內存泄漏的問題,因為運行時會定期回收未使用的內存。但是如果你以為這樣就完事大吉了,哪里就大錯特措了。

因為,雖然go中并未對字符串時候共享底層內存塊進行規定,但go語言編譯器/運行時默認情況下允許字符串共享底層內存塊,直到原先的字符串指向的內存被修改才會進行寫時復制,這是一個很好的設計,既能節省內存,又能節省CPU資源,但有時也會導致"內存泄漏"。

例如如下代碼,一旦調用demo就會導致將近1M內存的泄漏,因為s0只使用了50字節,但是會導致1M的內存一直無法被回收,這些內存會一直持續到下次s0被修改的時候才會被釋放掉。

var s0 string // a package-level variable// A demo purpose function.
func f(s1 string) {s0 = s1[:50]// Now, s0 shares the same underlying memory block// with s1. Although s1 is not alive now, but s0// is still alive, so the memory block they share// couldn't be collected, though there are only 50// bytes used in the block and all other bytes in// the block become unavailable.
}func demo() {s := createStringWithLengthOnHeap(1 << 20) // 1M bytesf(s)
}

為了避免這種內存泄漏,我們可以使用[]byte來替代原先的1M大小的內存,不過這樣會有兩次50字節的內存重復

func f(s1 string) {s0 = string([]byte(s1[:50]))
}

當然我們也可以利用go編譯器的優化來避免不必要的重復,只需要浪費一個字節內存就行

func f(s1 string) {s0 = (" " + s1[:50])[1:]
}

上述方法的缺點是編譯器優化以后可能會失效,并且其他編譯器可能無法提供該優化

避免此類內存泄漏的第三種方法是利用 Go 1.10 以來支持的 strings.Builder

import "strings"func f(s1 string) {var b strings.Builderb.Grow(50)b.WriteString(s1[:50])s0 = b.String()
}

從 Go 1.18 開始, strings 標準庫包中新增了 Clone 函數,這成為了完成這項工作的最佳方式。

子切片導致的內存泄漏

同樣場景下,切片也會導致內存的浪費

與子字符串類似,子切片也可能導致某種內存泄漏。在下面的代碼中,調用 g 函數后,保存 s1 元素的內存塊所占用的大部分內存將會丟失(如果沒有其他值引用該內存塊)。

var s0 []intfunc g(s1 []int) {// Assume the length of s1 is much larger than 30.s0 = s1[len(s1)-30:]
}

如果我們想避免這種內存泄漏,我們必須復制 s0 的 30 個元素,這樣 s0 的活躍性就不會阻止收集承載 s1 元素的內存塊。

func g(s1 []int) {s0 = make([]int, 30)copy(s0, s1[len(s1)-30:])// Now, the memory block hosting the elements// of s1 can be collected if no other values// are referencing the memory block.
}

未重置子切片指針導致的內存泄漏

在下面的代碼中,調用 h 函數后,為切片 s 的第一個和最后一個元素分配的內存塊將丟失。

func h() []*int {s := []*int{new(int), new(int), new(int), new(int)}// do something with s ...// 返回一個從1開始,不能到索引3的新切片, 也就是 s[1], s[2]return s[1:3:3]
}

只要返回的切片仍然有效,它就會阻止收集 s 的任何元素,從而阻止收集為 s 的第一個和最后一個元素引用的兩個 int 值分配的兩個內存塊。

如果我們想避免這種內存泄漏,我們必須重置丟失元素中存儲的指針。

func h() []*int {s := []*int{new(int), new(int), new(int), new(int)}// do something with s ...// Reset pointer values.s[0], s[len(s)-1] = nil, nilreturn s[1:3:3]
}

掛起Goroutine導致的內存泄漏

有時,Go 程序中的某些 goroutine 可能會永遠處于阻塞狀態。這樣的 goroutine 被稱為掛起的 goroutine。Go 運行時不會終止掛起的 goroutine,因此為掛起的 goroutine 分配的資源(以及它們引用的內存塊)永遠不會被垃圾回收。

Go 運行時不會殺死掛起的 Goroutine 有兩個原因。一是 Go 運行時有時很難判斷一個阻塞的 Goroutine 是否會被永久阻塞。二是我們有時會故意讓 Goroutine 掛起。例如,有時我們可能會讓 Go 程序的主 Goroutine 掛起,以避免程序退出。

如果不停止time.Ticker也會導致內存泄漏

time.Timer 值不再使用時,它會在一段時間后被垃圾回收。但 time.Ticker 值則不然。我們應該在 time.Ticker 值不再使用時停止它。

不正確地使用終結器會導致真正的內存泄漏

為屬于循環引用組的成員值設置終結器(finalizer)可能會阻止為該循環引用組分配的所有內存塊被回收。這是真正的內存泄漏,不是某種假象。

例如,在調用并退出以下函數后,分配給 xy 的內存塊不能保證在未來的垃圾收集中被收集。

func memoryLeaking() {type T struct {v [1<<20]intt *T}var finalizer = func(t *T) {fmt.Println("finalizer called")}var x, y T// The SetFinalizer call makes x escape to heap.runtime.SetFinalizer(&x, finalizer)// The following line forms a cyclic reference// group with two members, x and y.// This causes x and y are not collectable.x.t, y.t = &y, &x // y also escapes to heap.
}

因此,請避免為循環引用組中的值設置終結器。

延遲函數調用導致的某種資源泄漏

非常大的延遲調用堆棧也可能會消耗大量內存,并且如果某些調用延遲太多,某些資源可能無法及時釋放。

例如,如果在調用以下函數時需要處理許多文件,那么在函數退出之前將有大量文件處理程序無法釋放。

func writeManyFiles(files []File) error {for _, file := range files {f, err := os.Open(file.path)if err != nil {return err}defer f.Close()_, err = f.WriteString(file.content)if err != nil {return err}err = f.Sync()if err != nil {return err}}return nil
}

對于這種情況,我們可以使用匿名函數來封裝延遲調用,以便延遲函數調用能夠更早地執行。例如,上面的函數可以重寫并改進為

func writeManyFiles(files []File) error {for _, file := range files {if err := func() error {f, err := os.Open(file.path)if err != nil {return err}// The close method will be called at// the end of the current loop step.defer f.Close()_, err = f.WriteString(file.content)if err != nil {return err}return f.Sync()}(); err != nil {return err}}return nil
}

當然不要犯以下錯誤,需要有些同學將需要延時調用的函數字節省略,導致資源泄漏

_, err := os.Open(file.path)

如果是http請求,還會導致服務端擠壓大量的連接無法釋放

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/77704.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/77704.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/77704.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

es6學習02-let命令和const命令

一、let命令 1.let塊級作用域&#xff1a; let關鍵字 VS var關鍵字 2.for循環計數器很適合let命令 var&#xff1a;整個for循環中一直都是同一個i在做1&#xff0c;最后輸出的就是10&#xff1b; let&#xff1a;每循環一次都是多一個i的賦值&#xff0c;最后輸出是可以調出…

MySQL深分頁問題

在項目中有一個數據導出的需求&#xff0c;原來的實現方式也比較簡單&#xff0c;根據查詢條件分頁查所有的數據&#xff0c;然后轉成csv的格式一行一行寫進文件存儲中。 實際上線之后&#xff0c;發現出現了慢查詢&#xff0c;具體的sql如下&#xff1a; select * from tabl…

前端面試寶典---創建對象的配置

Object.create 對整個對象的多個屬性值進行配置 創建對象 不可更改屬性值 // 創建對象 不可更改屬性值 let obj Object.create({}, {name: {value: lisi,writable: false,},age: {value: 20,writable: true,} })console.log(初始化obj, obj) obj.name wangwu console.log(…

數據結構:C語言版嚴蔚敏和解析介紹,附pdf

《數據結構&#xff1a;C語言版&#xff08;第2版&#xff09;》嚴蔚敏李冬梅吳偉民.pdf 《數據結構&#xff1a;C語言版》嚴蔚敏&#xff0c;李冬梅.pdf 《數據結構C語言第2版習題解析與實驗指導》李冬梅.pdf 「《數據結構&#xff1a;C語言版&#xff08;第2版 &#xff09;》…

深入理解 v-for 指令及其使用方法

在 Vue.js 中&#xff0c;v-for 是用于渲染列表的核心指令&#xff0c;它允許你通過循環渲染數據源中的每一項。通過 v-for&#xff0c;你可以輕松地將數組、對象或其他可迭代的數據渲染成 HTML 元素。本文將詳細介紹 v-for 的基本用法、常見的應用場景、最佳實踐及性能優化&am…

VIRT, RES,SHR之間的關系

VIRT、RES 和 SHR 是進程內存使用的三個關鍵指標&#xff0c;它們之間的關系反映了進程的內存分配和使用情況。以下是它們的定義和關系&#xff1a; VIRT&#xff08;虛擬內存&#xff09;&#xff1a;表示進程分配的虛擬內存總量&#xff0c;包括所有代碼、數據、共享庫、堆棧…

2025屆藍橋杯JavaB組個人題解(暫時不全,沒題目)

2025 屆藍橋杯 Java B 組題解 第一次參加藍橋杯&#xff0c;輸入輸出都用的BufferedReader和PrintWriter&#xff0c;怕輸入輸出不對或者內存超限&#xff0c;也怕出現小錯誤運行不了的&#xff0c;比如Main打成Mian什么的&#xff0c;但還是希望能拿省一&#xff0c;這里給出自…

在Vue項目的引入meting-js音樂播放器插件

開源項目&#xff1a;https://github.com/swzaaaaaaa/NBlog 1、開源項目中音樂播放插件的使用流程 步驟1&#xff1a;下載meting-js相關文件 在MetingJS官方倉庫或其他可靠的CDN獲取meting-js的JavaScript文件以及相關依賴&#xff08;如APlayer的文件&#xff09;。將它們下…

HTML應用指南:利用GET請求獲取全國漢堡王門店位置信息

在當今快節奏的都市生活中&#xff0c;餐飲品牌的門店布局不僅反映了其市場策略&#xff0c;更折射出消費者對便捷、品質和品牌認同的追求。漢堡王&#xff08;Burger King&#xff09;作為全球知名的西式快餐品牌之一&#xff0c;在中國市場同樣占據重要地位。自進入中國市場以…

使用 Function 來編寫策略模式:優雅而高效的設計模式實踐

引言&#xff1a;為什么選擇策略模式&#xff1f; 策略模式&#xff08;Strategy Pattern&#xff09;是行為設計模式中的經典之一&#xff0c;它允許我們定義一系列的算法或操作&#xff0c;并使得它們可以互換使用。策略模式的關鍵思想是將算法的實現與使用它們的上下文分離…

Windows 系統中安裝 Git 并配置 GitHub 賬戶

由于電腦重裝系統&#xff0c;重新配置了git. 以下是在 Windows 系統中安裝 Git 并配置 GitHub 賬戶的詳細步驟&#xff1a; 1. 安裝 Git 訪問 Git 官網下載頁面下載 Windows 版本的 Git 安裝程序運行安裝程序&#xff0c;使用默認選項即可 2. 配置 Git 用戶信息 打開命令…

MergeX亮相GTC2025:開啟全球廣告流量交易新篇章

全球流量盛宴GTC2025深圳啟幕&#xff0c;共探出海新藍海 2025年4月24日至25日&#xff0c;GTC2025全球流量大會將在深圳福田會展中心9號館隆重召開。作為跨境出海領域內規模最大、資源最豐富、產業鏈最完備的年度盛會&#xff0c;此次大會將匯聚眾多行業精英&#xff0c;共同探…

kubernetes》》k8s》》Volume 數據卷 PVC PV NFS

為啥需要數據卷 容器磁盤上的文件的生命周期是短暫的&#xff0c;這就使得在容器中運行重要應用時會出現一些問題。首先&#xff0c;當容器崩潰時&#xff0c;kubelet會重啟它&#xff0c;但是容器中的文件將丟失——容器以干凈的狀態&#xff08;鏡像最初的狀態&#xff09;重…

第十六屆藍橋杯 省賽C/C++ 大學B組

編程題目現在在洛谷上都可以提交了。 未完待續&#xff0c;寫不動了。 C11 編譯命令 g A.cpp -o A -Wall -lm -stdc11A. 移動距離 本題總分&#xff1a;5 分 問題描述 小明初始在二維平面的原點&#xff0c;他想前往坐標 ( 233 , 666 ) (233, 666) (233,666)。在移動過程…

谷歌怎么設置在新標簽頁中打開網頁

按圖示操作即可&#xff0c;藏得真深啊&#xff0c;無語&#xff0c;而且就算打開了&#xff0c;點收藏夾&#xff0c;頂部快捷欄里的網站&#xff0c;網站里的連接&#xff0c;打開也還是覆蓋原來的&#xff0c;呵呵呵呵呵呵呵&#xff0c;有沒有人管管 另外我的edge不知咋滴…

【企業級數據安全】掌握高性能Log4j2敏感信息脫敏方案

前言 在數據安全合規日益嚴格的今天&#xff0c;日志中的敏感信息保護已成為企業IT建設的必備環節。本文帶您深入了解如何打造一套高性能、可實時配置的Log4j2日志脫敏插件&#xff0c;輕松應對各類敏感數據保護需求&#xff0c;讓您的系統既滿足合規要求&#xff0c;又不犧牲…

Linux中的tar -P選項

tar -P選項 Linux中的tar命令可用于文件和目錄的歸檔以及壓縮解壓縮。而其中的-P選項是什么含義呢&#xff1f;下面我們就來看一看 1、不添加-P選項 對于如下壓縮命令&#xff1a; tar -czvf pkg.tar.gz /opt/software執行該命名&#xff0c;控制臺首行輸出將會提示&#xf…

【2025年泰迪杯數據挖掘挑戰賽】B題 詳細解題思路+數據預處理+代碼分享

目錄 2025年泰迪杯B題詳細解題思路問題一問題分析數學模型Python代碼Matlab代碼 問題二問題分析數學模型Python代碼Matlab代碼 問題三問題分析數學模型Python代碼Matlab代碼 問題四問題分析數學模型Python代碼Matlab代碼 2025年泰迪杯B題詳細解題思路 初步分析整理了B題的賽題分…

SpringBoot3快速入門筆記

springboot3簡介 SpringBoot 幫我們簡單、快速地創建一個獨立的、生產級別的 Spring 應用&#xff08;說明&#xff1a;SpringBoot底層是Spring&#xff09; 大多數 SpringBoot 應用只需要編寫少量配置即可快速整合 Spring 平臺以及第三方技術 特性&#xff1a; ● 快速創建…

記錄centos8安裝寶塔過程(兩個腳本)

1、切換系統源&#xff08;方便使用寶塔安裝腳本下載&#xff09; bash <(curl -sSL https://linuxmirrors.cn/main.sh) 2、寶塔安裝腳本在寶塔的官網 寶塔面板下載&#xff0c;免費全能的服務器運維軟件 根據自己的系統選擇相應的腳本 urlhttps://download.bt.cn/insta…