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内存分配机制的资料请关注脚本之家其它相关文章!

相关文章

  • Go语言中如何进行数据库查询操作

    Go语言中如何进行数据库查询操作

    在Go语言中,与数据库交互通常通过使用数据库驱动来实现,Go语言支持多种数据库,如MySQL、PostgreSQL、SQLite等,每种数据库都有其对应的官方或第三方驱动,接下来通过本文给大家介绍Go语言中进行数据库查询操作方法,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • 一起来用GoLand开发第一个Go程序

    一起来用GoLand开发第一个Go程序

    当您在编辑器中工作时GoLand 会分析您的代码,寻找优化方法,并检测潜在和实际问题,下面这篇文章主要给大家介绍了关于用GoLand开发第一个Go程序的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • Go 中烧脑的接口及空接口

    Go 中烧脑的接口及空接口

    在Go语言的实际编程中,几乎所有的数据结构都围绕接口展开,接口是Go语言中所有数据结构的核心,这篇文章主要介绍了Go 中烧脑的接口,需要的朋友可以参考下
    2024-02-02
  • go mod 依赖管理的具体使用

    go mod 依赖管理的具体使用

    在Go语言开发中,依赖管理是一项非常重要的工作,Go mod作为官方的包管理工具已经成为了Go语言依赖管理的首选方式,本文就来介绍一下go mod 依赖管理的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Golang实现异步上传文件支持进度条查询的方法

    Golang实现异步上传文件支持进度条查询的方法

    这篇文章主要介绍了Golang实现异步上传文件支持进度条查询的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-10-10
  • Go语言标准输入输出库的基本使用教程

    Go语言标准输入输出库的基本使用教程

    输入输出在任何一门语言中都必须提供的一个功能,下面这篇文章主要给大家介绍了关于Go语言标准输入输出库的基本使用,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • Go语言实现二分查找方法示例

    Go语言实现二分查找方法示例

    这篇文章主要为大家介绍了Go语言实现二分查找方法示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • 总结Go语言中defer的使用和注意要点

    总结Go语言中defer的使用和注意要点

    Go语言中的defer关键字实现比较特殊的功能,这篇文章给大家总结了关于Go语言中defer的使用和注意要点,有需要的朋友们可以参考借鉴,下面来一起看看吧。
    2016-09-09
  • Go语言中如何确保Cookie数据的安全传输

    Go语言中如何确保Cookie数据的安全传输

    这篇文章主要介绍了Go语言中如何确保Cookie数据的安全传输,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-03-03
  • Golang将Map的键值对调的实现示例

    Golang将Map的键值对调的实现示例

    本文主要介绍了Golang将Map的键值对调的实现示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02

最新评论