golang 系列:啥是垃圾回收?

简介: golang 的三色标记法虽然没有 java 的内存回收机制成熟,但它细分了回收过程,通过写屏障技术,能和用户程序并发进行,这也一定程度的提高了内存回收速度。

摘要

golang 的三色标记法虽然没有 java 的内存回收机制成熟,但它细分了回收过程,通过写屏障技术,能和用户程序并发进行,这也一定程度的提高了内存回收速度。

一、为什么要有垃圾回收

我们都知道,当程序启动的时候,操作系统是会分配出栈区和堆区的,作为动态内存分配使用。

在栈区里分配的内存是可以自动管理的,一旦某个变量的作用域结束,就可以被自动回收了。

但是堆区就不是这样的了,堆区是属于程序员自己管理的区域,即使在某个作用域结束了,后续也能使用到该变量。

为此,程序员需要时刻关注内存的管理,否则将出现很多问题。例如内存一直在增长没有释放,则会出现内存溢出;内存释放后还继续访问,则会出现非法访问等。

因此,对内存的管理事关重要。然而,人为的管理内存始终存在隐患,谁也不能保证自己写的代码没有一点问题。

所以垃圾回收机制出现了,它是编程语言的设计者在程序运行时通过一定的策略,让闲置的内存被自动回收。

这也能让开发者更加专注于业务逻辑,减少额外的负担。

二、垃圾回收有哪些常用策略

1) 引用计数法

此算法为对象维护了一个计数值,当对象被引用时,计数值 +1,当对象的引用被释放时计数值 -1,直到计数值为 0 ,表示没有其他对象在使用它了,此时就可以进行回收动作了。

引用计数法算法简单,易于实现。但频繁的更新引用计数,也带来了一定的开销。而且对于循环引用的情况,计数值是归不了 0 的,此时就做不了回收了。

2) 标记-清除法

标记清除法是对对象定时的进行标记,分为正在被使用的和没有被使用这两类。

当标记完后就可以对没有被使用的这一类对象进行内存回收了。

标记清除法有个 Root 根对象,遍历搜索都是从 Root 根对象开始标记的。由于不存在对象跟 Root 循环引用的情况,所以总是能搜索到所有能达到的对象,依次标记。

如果循环对象没有被标记到,就表示没有被引用,就可以回收了,循环引用问题就解决了。

由于程序是动态在运行的,随时有可能会改变对象的引用指向。因此,在进行标记动作的时候需要 Stop the world(STW),也就是停止其他任务的执行,使得标记过程没有被打乱。

这也就意味着程序会短暂的停滞,对于响应要求高的程序而言,无疑是不能接受的。

三、golang 的垃圾回收

golang 采用了叫三色标记法的回收机制,它是第二种算法的变种,通过将标记清除过程细分了多个阶段,并采用了写屏障去感知引用的修改,使得垃圾回收动作能和用户程序并发的进行,大大缩短了 Stop The World 的同时,也保证了对象不被误清除。

三色标记法将对象分成了三种:

  • 白色对象:未被使用的对象;
  • 灰色对象:当前对象有引用对象,但是还没有对引用对象继续扫描过;
  • 黑色对象,对上面提到的灰色对象的引用对象已经全部扫描过了,下次不用再扫描它的引用对象了。

当垃圾回收开始时,Go 会把根对象标记为灰色,其他对象标记为白色,然后从根对象遍历搜索,按照上面的定义去不断的对灰色对象进行扫描标记。
(这里的根对象可以理解为指向堆内存区块的指针)

当没有灰色对象时,表示标记完成,然后就可以开始清除白色对象了。

三色标记法在正式标记前会进行 Stop The World,以便启动写屏障。当启动好后,就会停止 Stop The World。然后开始标记对象,在这标记过程中,是可以和用户程序一起并发执行的。

当所有的对象都标记完,也就是没有灰色对象可遍历搜索时,会再一次的 STW,做第二次的扫描。

利用之前启动的写屏障,将标记期间有过引用修改的对象重新标记为灰色,保证对象不会被误清。

当第二次扫描结束时,就可以开始真正的清除动作了,而且也是可以跟用户程序一起并发执行的。

后面用户程序即使再 new 了对象,分配了内存,也不会进行标记动作了,相当于这些新的对象是下次 GC 要处理的了。

而对于原先被标记为白色的对象,也就是再也没有被使用的对象,程序是引用不到的了。此时就可以大胆并发清除,不需要再次 Stop the world 了。

触发时机

Go 允许手动触发垃圾回收,但一般开发者比较少介入内存的管理,更多是让运行时 runtime 根据下面 2 种情况来进行垃圾回收:

  • 内存分配到一定大小时触发
  • 一定时间内没有触发过垃圾回收,则会开始进行 GC,一般这个时间是 2 分钟

结尾

虽然有 GC 帮我们做内存的管理,但资源不是无限的,一旦内存上涨,那我们就得学会查找问题了。

当内存一直没有释放时,我们可以使用 pprof 这些性能分析工具,帮助我们去做内存分析。

另外,在写代码时我们也可以从下面几个点进行内存的优化,让我们的程序更加健壮。

  • string 和 []byte 可以通过 unsafe 或 reflect 包进行强制转换,以减少内存拷贝。
  • 如果频繁的临时对象需要创建,则可以使用 sync.Pool 来重用对象。减少垃圾回收。
  • 如果能提前知道 slice 的大小,尽量预分配好它的容量,避免不断的 append 中,不断的扩容。
相关文章
|
3月前
|
Java Go
Golang底层原理剖析之垃圾回收GC(二)
Golang底层原理剖析之垃圾回收GC(二)
78 0
|
10月前
|
算法 Java Go
每位 Gopher 都应该了解的 Golang 语言的垃圾回收算法
每位 Gopher 都应该了解的 Golang 语言的垃圾回收算法
28 0
|
3月前
|
算法 Java Go
Go vs Java:内存管理与垃圾回收机制对比
对比了Go和Java的内存管理与垃圾回收机制。Java依赖JVM自动管理内存,使用堆栈内存并采用多种垃圾回收算法,如标记-清除和分代收集。Go则提供更多的手动控制,内存分配与释放由分配器和垃圾回收器协同完成,使用三色标记算法并发回收。示例展示了Java中对象自动创建和销毁,而Go中开发者需注意内存泄漏。选择语言应根据项目需求和技术栈来决定。
|
14天前
|
算法 安全 Java
|
3月前
|
缓存 算法 安全
浅谈go垃圾回收与竞争检测
【5月更文挑战第16天】Go语言的运行时聚焦于垃圾回收(GC)和并发特性。GC通过微小和小对象分配器管理内存,大于32KB的大对象直接分配。GC是并发的,使用写屏障和非压缩策略,分为扫描终止、标记、标记终止和扫除四个阶段。竞争检测用于查找数据竞争,debug包提供运行时调试功能,如堆栈跟踪。内部的atomic包提供原子操作保证线程安全,math包检测数学溢出。sys包包含系统特定常量,NotInHeap结构确保某些对象不被GC管理。
57 5
浅谈go垃圾回收与竞争检测
|
3月前
|
Java Go
golang垃圾回收
Go语言1.3前采用标记清除的垃圾回收,1.5后引入三色标记法(白、灰、黑)解决长时间停止问题。三色标记通过强弱不变式避免对象丢失,保证垃圾回收准确性。过程包括从根节点遍历将对象从白转灰,再转黑,最终回收白色对象。为防止对象丢失,使用插入和删除写屏障,但插入屏障可能导致短暂暂停,结束时仍需STW扫描栈。删除屏障保护灰色到白色对象的路径不断。
31 1
|
3月前
|
算法 Java 大数据
深入理解Go的垃圾回收机制
深入理解Go的垃圾回收机制
74 0
|
3月前
|
存储 算法 Java
Go语言GC(垃圾回收)的工作原理
【2月更文挑战第23天】
101 0
|
12月前
|
算法 Java Go
Go的垃圾回收器
go的垃圾回收器是怎么回收内存的?
50 0
|
3月前
|
监控 Java 编译器
优化Go语言程序中的内存使用与垃圾回收性能
【2月更文挑战第5天】本文旨在探讨如何优化Go语言程序中的内存使用和垃圾回收性能。我们将深入了解内存分配策略、垃圾回收机制,并提供一系列实用的优化技巧和建议,帮助开发者更有效地管理内存,减少垃圾回收的开销,从而提升Go程序的性能。