首页 > 其他分享 >golang gc介绍

golang gc介绍

时间:2023-02-07 14:45:24浏览次数:51  
标签:触发 Alloc 14 介绍 golang TotalAlloc gc go GC

1.什么是 GC

在计算机科学中,垃圾回收(GC)是一种自动管理内存的机制,垃圾回收器会去尝试回收程序不再使用的对象及其占用的内存。

最早 John McCarthy 在 1959 年左右发明了垃圾回收,以简化 Lisp 中的手动内存管理的机制(来自 @wikipedia)。

2.为什么要 GC

手动管理内存挺麻烦,管错或者管漏内存也很糟糕,将会直接导致程序不稳定(持续泄露)甚至直接崩溃。

3. GC 触发场景

GC 触发的场景主要分为两大类,分别是:

  1. 系统触发:运行时自行根据内置的条件,检查、发现到,则进行 GC 处理,维护整个应用程序的可用性。

  2. 手动触发:开发者在业务代码中自行调用 runtime.GC 方法来触发 GC 行为。

3.1.系统触发

在系统触发的场景中,Go 源码的 src/runtime/mgc.go 文件,明确标识了 GC 系统触发的三种场景,分别如下:

const (

     gcTriggerHeap gcTriggerKind = iota

     gcTriggerTime

     gcTriggerCycle

)
  • gcTriggerHeap:当所分配的堆大小达到阈值(由控制器计算的触发堆的大小)时,将会触发。

  • gcTriggerTime:当距离上一个 GC 周期的时间超过一定时间时,将会触发。-时间周期以 runtime.forcegcperiod 变量为准,默认 2 分钟。

  • gcTriggerCycle:如果没有开启 GC,则启动 GC。

  • 在手动触发的 runtime.GC 方法中涉及。

3.2. 手动触发

在手动触发的场景下,Go 语言中仅有 runtime.GC 方法可以触发,也就没什么额外的分类的。

runtime.GC()

但我们要思考的是,一般我们在什么业务场景中,要涉及到手动干涉 GC,强制触发他呢?

需要手动强制触发的场景极其少见,可能会是在某些业务方法执行完后,因其占用了过多的内存,需要人为释放。又或是 debug 程序所需。

4. GC 实例

4.1.系统触发

package main

import (
	"log"
	"runtime"
	"time"
)

var lastTotalFreed uint64
var intMap map[int]int
var cnt = 8192

func printMemStats(){
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	log.Printf("Alloc = %v TotalAlloc=%v Just Freed = %v Sys = %v NumGc=%v\n",
		m.Alloc/1024, m.TotalAlloc/1024, ((m.TotalAlloc - m.Alloc)-lastTotalFreed)/1024, m.Sys/1024, m.NumGC)
	lastTotalFreed = m.TotalAlloc - m.Alloc
}

func initMap(){
	intMap = make(map[int]int, cnt)

	for i:=0; i<cnt;i++{
		intMap[i]= i
	}
}

func main() {
	printMemStats()

	initMap()
	runtime.GC()
	printMemStats()

	log.Println(len(intMap))

	for i:=0; i<cnt; i++{
		delete(intMap, i)
	}
	log.Println(len(intMap))

	runtime.GC()
	printMemStats()

	intMap = nil
	// 等待2分钟自动gc
	for {
		time.Sleep(1 * time.Minute)
		printMemStats()
	}
}
3分钟左右内存就释放了
 GOROOT=/usr/local/go #gosetup
GOPATH=/Users/qicycle/Go #gosetup
/usr/local/go/bin/go build -o /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go -gcflags all=-N -l /System/Volumes/Data/Users/qicycle/Documents/我的测试/golang/main_gc.gosetup
/Applications/GoLand.app/Contents/plugins/go/lib/dlv/mac/dlv --listen=0.0.0.0:51694 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go --
API server listening at: [::]:51694
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-1100.0.30..1
 for x86_64.
Got a connection, launched process /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go (pid = 3497).
2023/02/07 14:17:44 Alloc = 66 TotalAlloc=66 Just Freed = 0 Sys = 12143 NumGc=0
2023/02/07 14:17:44 Alloc = 380 TotalAlloc=383 Just Freed = 3 Sys = 12719 NumGc=1
2023/02/07 14:17:44 8192
2023/02/07 14:17:44 0
2023/02/07 14:17:44 Alloc = 380 TotalAlloc=384 Just Freed = 1 Sys = 12783 NumGc=2
2023/02/07 14:18:44 Alloc = 381 TotalAlloc=385 Just Freed = 0 Sys = 12783 NumGc=2
2023/02/07 14:19:44 Alloc = 381 TotalAlloc=385 Just Freed = 0 Sys = 12783 NumGc=2
2023/02/07 14:20:44 Alloc = 68 TotalAlloc=386 Just Freed = 313 Sys = 12783 NumGc=3
Exiting.

可以看到大概等待3分钟左右,系统就自动释放了临时申请的内存:

14:17:44的时候Alloc是66,14:20:44时候Alloc是68

4.2. 手动触发

package main

import (
	"log"
	"runtime"
)

var lastTotalFreed uint64
var intMap map[int]int
var cnt = 8192

func printMemStats(){
	var m runtime.MemStats
	runtime.ReadMemStats(&m)
	log.Printf("Alloc = %v TotalAlloc=%v Just Freed = %v Sys = %v NumGc=%v\n",
		m.Alloc/1024, m.TotalAlloc/1024, ((m.TotalAlloc - m.Alloc)-lastTotalFreed)/1024, m.Sys/1024, m.NumGC)
	lastTotalFreed = m.TotalAlloc - m.Alloc
}

func initMap(){
	intMap = make(map[int]int, cnt)

	for i:=0; i<cnt;i++{
		intMap[i]= i
	}
}

func main() {
	printMemStats()

	initMap()
	runtime.GC()
	printMemStats()

	log.Println(len(intMap))

	for i:=0; i<cnt; i++{
		delete(intMap, i)
	}
	log.Println(len(intMap))

	runtime.GC()
	printMemStats()

	intMap = nil

	// 手动gc
	runtime.GC()
	printMemStats()
}
手动gc立刻释放内存
 /usr/local/go/bin/go build -o /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go -gcflags all=-N -l /System/Volumes/Data/Users/qicycle/Documents/我的测试/golang/main_gc.gosetup
/Applications/GoLand.app/Contents/plugins/go/lib/dlv/mac/dlv --listen=0.0.0.0:51917 --headless=true --api-version=2 --check-go-version=false --only-same-user=false exec /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go --
API server listening at: [::]:51917
debugserver-@(#)PROGRAM:LLDB  PROJECT:lldb-1100.0.30..1
 for x86_64.
Got a connection, launched process /private/var/folders/26/ynhz7g5n3xg19q_bnpqcjpdc0000gn/T/___go_build_main_gc_go (pid = 3664).
2023/02/07 14:34:48 Alloc = 68 TotalAlloc=68 Just Freed = 0 Sys = 12399 NumGc=0
2023/02/07 14:34:48 Alloc = 382 TotalAlloc=385 Just Freed = 3 Sys = 12719 NumGc=1
2023/02/07 14:34:48 8192
2023/02/07 14:34:48 0
2023/02/07 14:34:48 Alloc = 382 TotalAlloc=386 Just Freed = 1 Sys = 12783 NumGc=2
2023/02/07 14:34:48 Alloc = 69 TotalAlloc=387 Just Freed = 313 Sys = 13039 NumGc=3
Exiting.

 

可以看到gc以后内存立刻释放了:

14:34:48的时候Alloc是68,14:34:48最后一条时候Alloc是69

 

结论:需要手动强制触发的场景极其少见,可能会是在某些业务方法执行完后,因其占用了过多的内存,需要人为释放。又或是 debug 程序所需

标签:触发,Alloc,14,介绍,golang,TotalAlloc,gc,go,GC
From: https://www.cnblogs.com/zhanchenjin/p/17098294.html

相关文章

  • .NET(C#、VB)APP开发——Smobiler平台控件介绍:MarqueeLabel
    本文简述如何在Smobiler中使用MarqueeLabel。Step1.新建一个SmobilerForm窗体,并在窗体中加入MarqueeLabel,控件Size设置(300,30),布局如下MarqueelLabel可以直接在设计器......
  • 图表控件LightningChart.NET 系列教程(八):LightningChart 组件——从工具箱添加至 Windo
    LightningChar​t.NET​ SDK是一款高性能数据可视化插件工具,由数据可视化软件组件和工具类组成,可支持基于Windows 的用户界面框架(WindowsPresentationFoundation)、Win......
  • golang 内存泄漏总结
    1.内存泄漏归纳简单归纳一下,还是“临时性”内存泄露和“永久性”内存泄露:临时性泄露,指的是该释放的内存资源没有及时释放,对应的内存资源仍然有机会在更晚些时候被释放,即......
  • 知识付费平台,电商平台,内容平台介绍
    知识付费平台,电商平台,内容平台1.知识付费平台*千聊*荔枝微课*有讲*维库*网易云课堂*腾讯课堂*得到*混沌大学*喜马拉雅*十点课程*凯叔讲故事*......
  • golang 线程和系统线程的的区别
    和操作系统的线程调度不同的是,Go调度器并不是用一个硬件定时器而是被Go语言"建筑"本身进行调度的。例如当一个goroutine调用了time.Sleep或者被channel调用或者mutex操作阻......
  • golang 字符串
    字符串常用系统函数1.len(str):统计字符串长度这个函数是内建函数,存在于内建包builtin中,可以不用导入直接使用。golang的编码统一为utf-8(ascii的字符(字母和数字)占一个字......
  • linux的 bus、device、driver介绍
    linux通过device和driver分别管理系统中的设备和驱动,用bus将设备和驱动关联起来,bus可以看成是设备和驱动的媒介,可以匹配设备和驱动。这样设备和驱动可以独立加载,互不......
  • 自我介绍
    1、自我介绍基本信息姓名:彭俊豪性别:男,来自广东河源。我性格较为开朗,喜欢与他人协作完成某些事。平时喜欢打桌球,并且喜欢钻研有关计算机的内容。在大学的第一个学......
  • OushuDB 产品基本介绍——表
    1、表OushuDB表由行(rows)和(columns)组成。每一个列有一个列名和一个数据类型,一个表的列数和列的顺序是固定的。一个表的行数是可变的。SQL并不假设表中行的顺序。当读一个表......
  • golang defer
    packagemainimport("fmt")//defer的最佳实践是,当函数执行完毕后,可以及时的释放函数创建的资源//在前面先写deferfile.close(),先把defer压入栈不执行,先执行......