Golang内存分配机制详解

 更新时间:2023年12月20日 08:59:30   作者:路多辛  
Go 语言的内存分配机制是理解和优化 Go 程序性能的关键,在 Go 中,内存管理是自动进行的,这得益于 Go 的垃圾回收机制,了解内存如何分配和回收,可以帮助我们写出更高性能的代码,本文将深入讲解下 Go 内存分配机制,需要的朋友可以参考下

内存分配的基本原理

在计算机科学中,内存分配是指为程序中的变量和数据结构分配存储空间的过程。在 Golang 中,内存分配主要由 Go 运行时系统(runtime)负责,主要包括以下两个方面:

  • 堆内存分配:堆内存是用于动态分配的内存区域,主要用于存储程序运行过程中创建的对象和数据结构。通过 new、make 等函数或者使用指针进行内存分配时,都会在堆上分配内存。
  • 栈内存分配:栈内存是用于存放函数调用时的局部变量和返回地址等信息的内存区域。基本类型(如整型、浮点型、布尔型等)和小对象(通常小于 64 字节)的内存分配通常发生在栈上。

Go 使用了一个称为“tcmalloc”(thread-caching malloc)的内存分配器,最初由 Google 开发。tcmalloc 的设计目标是减少全局锁的竞争,提高多线程程序的性能。Go 的内存分配器是基于 tcmalloc 概念的一个实现,有几个关键的组成部分:

  • M: 代表操作系统线程(machine)。
  • P: 代表处理器(processor),管理着一组本地缓存。
  • G: 代表 goroutine,是 Go 程序执行的最小单位。

每个 P 都有自己的内存缓存(mcache),用于小对象的快速分配。当 mcache 用完时,P 会从中心缓存(mcentral)获取内存。如果 mcentral 也不足,内存分配器会向操作系统请求更多内存。

Golang 内存分配的机制

  • 小对象分配,小对象(一般小于 32KB)的分配是通过 P 的 mcache 来进行的。mcache 包含了一系列固定大小的内存块,称为“span”。每个 span 专门用于一种大小的对象。当一个 goroutine 需要分配一个小对象时,就会查找对应大小的 span,并从中分配一块内存。
  • 大对象分配,大对象(一般大于 32KB)的分配不经过 mcache,而是直接从堆上分配。这是因为大对象的分配和回收比小对象更少,直接在堆上操作可以减少碎片和管理的复杂性。
  • 内存分配优化,为了减少内存分配的成本,Go 的内存分配器会进行一些优化:
    • 大小类分配:为了减少内存碎片和提高内存重用,Go 将对象分为不同的大小类。每个大小类的对象都会分配到对应的 span 中。
    • 对象对齐:Go 保证对象在内存中对齐,有助于提高 CPU 缓存的效率。
    • 批量分配:当 mcache 中的 span 用完时,不是一次只获取一个新的 span,而是批量地从 mcentral 获取多个 span,可以减少与 mcentral 的交互次数。

垃圾回收(GC)

Go 的垃圾回收器是一个并发的、标记-清除(mark-sweep)垃圾回收器。垃圾回收分为几个阶段:

  • 标记阶段:垃圾回收器会停止所有的 goroutine(STW - stop the world),快速扫描栈和全局变量,标记所有可达的对象。
  • 并发标记:goroutine 被恢复执行,同时垃圾回收器在后台并发地完成标记工作。
  • 清除阶段:清除未被标记的对象,通常也是并发进行的。

Go 的垃圾回收器设计为低延迟,会尽量减少对程序执行的干扰。

内存逃逸

在 Go 中,编译器会尽量在栈上分配内存,因为栈上的内存分配和回收非常快。然而,并不是所有的内存分配都可以在栈上完成。当编译器无法保证对象的生命周期仅限于其定义的作用域时,会将这些对象分配到堆上,这个过程称为“内存逃逸”。

内存分配的影响因素

内存分配的性能可能受到多种因素的影响,包括以下几个方面:

  • 内存分配频率:频繁的内存分配和回收会增加垃圾回收器的工作量,从而影响性能。
  • 对象大小:大对象的分配通常比小对象慢,因为不经过 mcache。
  • 对象生命周期:长生命周期的对象可能会导致内存占用增加,因为不会被频繁回收。

内存分配的最佳实践

为了优化内存分配,可以从以下几个方面着手:

  • 重用对象:通过重用对象来减少分配次数。
  • 池化资源:使用 sync.Pool 来池化可重用的对象。
  • 避免内存逃逸:通过减少指针的使用和闭包捕获来避免不必要的内存逃逸。
  • 合理的数据结构:选择合适的数据结构来减少内存的占用和碎片。

小结

Go 的内存分配器是为并发和多线程设计的,通过一系列优化来提供高效的内存分配。内存分配的性能不仅取决于分配器本身,还取决于程序的设计和编码方式。在日常开发中,可以使用工具(如 pprof)来分析和优化程序的内存使用情况。通过实践和分析,更深入地理解和掌握 Go 的内存管理机制。

以上就是Golang内存分配机制详解的详细内容,更多关于Golang内存分配机制的资料请关注脚本之家其它相关文章!

相关文章

  • 夯实Golang基础之数据类型梳理汇总

    夯实Golang基础之数据类型梳理汇总

    这篇文章主要8为大家介绍了夯实Golang基础之数据类型梳理汇总,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2023-10-10
  • golang gorm错误处理事务以及日志用法示例

    golang gorm错误处理事务以及日志用法示例

    这篇文章主要为大家介绍了golang gorm错误处理事务以及日志用法示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-04-04
  • Go语言基础学习教程

    Go语言基础学习教程

    这篇文章主要介绍了Go语言基础知识,包括基本语法、语句、数组等的定义与用法,需要的朋友可以参考下
    2016-07-07
  • go env环境变量配置的使用

    go env环境变量配置的使用

    在安装和使用Go时,必须要正确地配置环境变量,本文主要介绍了go env环境变量配置的使用,具有一定的参考价值,感兴趣的可以了解一下
    2023-11-11
  • 使用go module导入本地包的方法教程详解

    使用go module导入本地包的方法教程详解

    go module 将是Go语言默认的依赖管理工具。到今天 Go1.14 版本推出之后 Go modules 功能已经被正式推荐在生产环境下使用了。本文重点给大家介绍如何使用 go module 导入本地包,感兴趣的朋友一起看看吧
    2020-03-03
  • 使用Gomock进行单元测试的方法示例

    使用Gomock进行单元测试的方法示例

    这篇文章主要介绍了使用Gomock进行单元测试的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-11-11
  • Golang必知必会之Go Mod命令详解

    Golang必知必会之Go Mod命令详解

    go mod可以使项目从GOPATH的强制依赖中独立出来,也就是说你的项目依赖不再需要放在在GOPATH下面了,下面这篇文章主要给大家介绍了关于Golang必知必会之Go Mod命令的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Go语言中的流程控制结构和函数详解

    Go语言中的流程控制结构和函数详解

    这篇文章主要介绍了Go语言中的流程控制结构和函数详解,本文详细讲解了if、goto、for、switch等控制语句,同时对函数相关知识做了讲解,需要的朋友可以参考下
    2014-10-10
  • Golang中goroutine和channel使用介绍深入分析

    Golang中goroutine和channel使用介绍深入分析

    一次只做一件事情并不是完成任务最快的方法,一些大的任务可以拆解成若干个小任务,goroutine可以让程序同时处理几个不同的任务,goroutine使用channel来协调它们的工作,channel允许goroutine互相发送数据并同步,这样一个goroutine就不会领先于另一个goroutine
    2023-01-01
  • GO语言中回调函数的使用

    GO语言中回调函数的使用

    本文主要介绍了GO语言中回调函数的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03

最新评论