Golang内存、指针逃逸、垃圾回收机制概览

2024-04-19 23:12

本文主要是介绍Golang内存、指针逃逸、垃圾回收机制概览,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近看到了一篇文章是关于go的内存、指针逃逸和垃圾回收机制的,发现自己并未很细致的了解过这方面的内容,于是在翻阅各种文章的情况下,写出了这篇总结,参考文章放在文末,可自取

内存

Go 语言使用一个自带的垃圾收集器(Garbage Collector, GC)来自动管理内存,这意味着程序员不需要直接参与内存的分配和释放,这减少了内存泄漏和其他内存相关错误的可能性。Go 中的内存可以分为两个主要部分:

  1. 栈(Stack):栈通常存储大小生命周期是能被预估的数据。函数内的局部变量和返回值;管理采用先进后出的模式,不需要复杂的垃圾回收机制;栈的特点是拥有非常高的访问速度和较低的内存分配开销,但空间有限。
  2. 堆(Heap):用于存储运行时可能变化的数据,或函数作用域之外需要访问的数据;更大规模的内存区域,用于存储生命周期较长或大小无法预知的数据。堆内存的分配和回收成本相对较高,但可以动态地扩展。

可以通过以下命令来分析应用程序:

go build -gcflags=-m main.go

指针逃逸

指针逃逸分析是 Go 编译器进行的一种优化。通过这种分析,编译器确定变量的存储位置(栈还是堆)。如果一个变量在函数结束后仍然可以被访问(例如,被其他函数引用或返回给调用者),这个变量就会从栈“逃逸”到堆。

指针逃逸的主要影响是性能:

  • 栈分配的变量:当函数调用结束时,这些变量的内存可以立即被清理,这一过程非常快速且高效。
  • 堆分配的变量:需要垃圾收集器介入来回收这部分内存,这可能导致额外的性能开销。

深入理解指针逃逸

在 Go 中,编译器进行逃逸分析是为了决定数据应当存放在堆上还是栈上。我们已经知道,存放在栈上的数据有着更快的访问速度和更简单的生命周期管理,但栈的空间有限且仅在函数执行期间存在。相反,堆上的数据可以在函数执行完毕后继续存在,但其管理成本较高,因为涉及到复杂的垃圾回收机制。

何时发生逃逸?

  1. 返回局部变量的地址:如果函数返回局部变量的指针,这个变量就会从函数的栈帧中逃逸到堆,因为局部变量的生命周期必须延长到函数外部。
  2. 大对象:即使对象没有被外部引用,如果对象非常大,它可能也会被分配到堆上,以避免栈溢出。
  3. 动态类型:如接口或含有接口的类型。由于接口的动态特性,编译器可能无法预测具体的实现类型和大小,因此可能选择将其分配到堆上。
  4. 闭包:引用外部函数局部变量的闭包可能导致这些变量逃逸,因为这些变量必须在闭包存在时继续存在。

优化技巧

理解和优化指针逃逸可以使得 Go 程序更加高效。以下是一些常见的优化技巧:

  • 避免不必要的堆分配:尽量使用局部变量和传值,避免在不必要的情况下创建指针。
  • 使用对象池:对于频繁使用和创建的对象,可以使用 sync.Pool 来复用对象,减少垃圾收集的负担。
  • 分析逃逸情况:使用 go build -gcflags="-m" 命令来查看编译器的逃逸分析结果,了解哪些变量逃逸到堆,并探索优化方法。
  • 配置垃圾收集器:通过设置 GOGC 环境变量(默认值是 100),可以调整垃圾收集器的敏感度。增加这个值会减少垃圾收集的频率,可能增加程序的整体内存使用,但可以减少因垃圾收集引起的延迟。

垃圾回收机制

Go语言的垃圾回收(GC)机制是一种自动内存管理的实现,它旨在帮助程序开发者免除手动管理内存的复杂性。Go的垃圾回收器主要基于“标记-清扫”(Mark-and-Sweep)算法,但随着版本的更新,Go团队已经对其进行了优化和改进,引入了并发的执行和更多的性能优化措施。Go的GC实现的特点是并发执行,且尽量减少对程序执行的干扰。

overview

设计原则

Go的垃圾回收器设计目标是简化并发程序的内存管理,同时实现以下几个关键目标:

  1. 效率:尽量减少GC的CPU和内存开销。
  2. 并发:GC过程与用户程序并发执行,减少STW(Stop-The-World)的影响。
  3. 实时性:保证程序的响应时间,通过减少GC引起的延迟。

垃圾回收器

垃圾回收器中的变量通常分为以下三类:

  • 活动堆内存(在上一次垃圾回收周期中标记为“活动”的内存)
  • 新堆内存(尚未由垃圾回收器分析的堆内存)
  • 内存用于存储一些元数据,通常与前两个实体相比微不足道。

垃圾回收器的CPU时间消耗与其工作特性有关。有一种称为“全停顿”的垃圾回收器实现,它会在垃圾回收期间完全停止程序执行,导致CPU时间用于非生产性工作。

在Go的情况下,垃圾回收器并非完全“全停顿”,并且在应用程序执行过程中并行执行大部分工作,例如堆标记。

然而,垃圾回收器仍然有一些限制,并且在一个周期内多次完全停止执行工作代码。

垃圾收集的性能开销和内存使用效率直接关联到逃逸分析的结果。减少堆分配可以显著降低垃圾收集的频率和延迟,从而提高程序的整体性能。

核心算法

Go 的垃圾收集器是一个实现了三色标记清除算法的并发收集器。垃圾收集过程主要分为以下几个阶段:

初始化阶段

GC的启动通常由内存分配触发,当分配的总内存量达到当前堆大小的一定比例(由**GOGC**环境变量控制,默认为100%)时,GC开始工作。

标记阶段(Mark Phase)

在这一阶段,垃圾回收器通过从根对象(如全局变量和当前所有Goroutine的栈)出发,标记所有可达的对象。Go使用写屏障(write barrier),在运行时对对象进行标记,这有助于垃圾回收器在应用程序运行时并发执行。

  • 三色抽象:使用黑色、灰色和白色来代表不同状态的对象:
    • 黑色:对象及其子对象都已经被扫描,不会再引用新的白色对象。
    • 灰色:对象被标记为存活,但其子对象还未扫描完。
    • 白色:对象未被访问,可能是垃圾。

gif

清扫阶段(Sweep Phase)

标记完成后,GC进入清扫阶段。在这个阶段,GC遍历堆中的所有对象,释放那些标记为白色的对象所占用的内存。清扫阶段通常也是并发进行,不会中断程序的正常执行。

如何管理垃圾回收器

有一个参数允许您在Go中管理垃圾回收器:GOGC环境变量或其功能等效项SetGCPercent,来自runtime/debug包。

GOGC参数决定了在触发垃圾回收时相对于活动内存的新未分配堆内存的百分比。

GOGC的默认值为100,这意味着当新内存的数量达到活动堆内存的100%时,将触发垃圾回收。

优化和改进

并发垃圾回收

Go的GC从版本1.5开始实施并发标记,这显著降低了STW的时间。在最近的版本中,Go团队进一步减少了GC操作中必须停止程序执行的时间。

写屏障

写屏障是用来维护GC标记正确性的技术。当程序运行时修改对象引用时,写屏障确保这些改动不会破坏正在进行的垃圾回收过程。Go使用的是混合写屏障,它在GC期间启用,有助于标记阶段的并发执行。

调节和配置

  • GOGC环境变量:通过设置这个环境变量,开发者可以控制GC触发的频率。增大这个值会增加堆的允许大小,从而减少GC的频率,反之亦然。
  • runtime/debug 包:提供了更细粒度的控制,比如**SetGCPercent**函数允许在运行时调整GC的触发阈值。

性能考量

尽管Go的GC是高度优化的,但在内存密集或延迟敏感的应用中,GC仍可能成为性能瓶颈。开发者需要通过剖析工具(如pprof)定期检查GC的性能影响,并适当调整GC配置以优化应用性能。

参考文章

  1. Golang垃圾回收(GC)介绍
  2. Memory Optimization and Garbage Collector Management in Go

这篇关于Golang内存、指针逃逸、垃圾回收机制概览的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/918698

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

NameNode内存生产配置

Hadoop2.x 系列,配置 NameNode 内存 NameNode 内存默认 2000m ,如果服务器内存 4G , NameNode 内存可以配置 3g 。在 hadoop-env.sh 文件中配置如下。 HADOOP_NAMENODE_OPTS=-Xmx3072m Hadoop3.x 系列,配置 Nam

离心萃取机废旧磷酸铁锂电池回收工艺流程

在废旧磷酸铁锂电池的回收工艺流程中,离心萃取机主要应用于萃取除杂的步骤,以提高回收过程中有价金属(如锂)的纯度。以下是结合离心萃取机应用的废旧磷酸铁锂电池回收工艺流程: 电池拆解与预处理 拆解:将废旧磷酸铁锂电池进行拆解,分离出电池壳、正负极片、隔膜等部分。破碎与筛分:将正负极片进行破碎处理,并通过筛分将不同粒径的物料分开,以便后续处理。 浸出与溶解 浸出:采用适当的浸出工艺(如二段式逆

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

【编程底层思考】垃圾收集机制,GC算法,垃圾收集器类型概述

Java的垃圾收集(Garbage Collection,GC)机制是Java语言的一大特色,它负责自动管理内存的回收,释放不再使用的对象所占用的内存。以下是对Java垃圾收集机制的详细介绍: 一、垃圾收集机制概述: 对象存活判断:垃圾收集器定期检查堆内存中的对象,判断哪些对象是“垃圾”,即不再被任何引用链直接或间接引用的对象。内存回收:将判断为垃圾的对象占用的内存进行回收,以便重新使用。

【Tools】大模型中的自注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 自注意力机制(Self-Attention)是一种在Transformer等大模型中经常使用的注意力机制。该机制通过对输入序列中的每个元素计算与其他元素之间的相似性,

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

【C++学习笔记 20】C++中的智能指针

智能指针的功能 在上一篇笔记提到了在栈和堆上创建变量的区别,使用new关键字创建变量时,需要搭配delete关键字销毁变量。而智能指针的作用就是调用new分配内存时,不必自己去调用delete,甚至不用调用new。 智能指针实际上就是对原始指针的包装。 unique_ptr 最简单的智能指针,是一种作用域指针,意思是当指针超出该作用域时,会自动调用delete。它名为unique的原因是这个

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

【Tools】大模型中的注意力机制

摇来摇去摇碎点点的金黄 伸手牵来一片梦的霞光 南方的小巷推开多情的门窗 年轻和我们歌唱 摇来摇去摇着温柔的阳光 轻轻托起一件梦的衣裳 古老的都市每天都改变模样                      🎵 方芳《摇太阳》 在大模型中,注意力机制是一种重要的技术,它被广泛应用于自然语言处理领域,特别是在机器翻译和语言模型中。 注意力机制的基本思想是通过计算输入序列中各个位置的权重,以确