当前位置: 首页 > news >正文

go垃圾回收

1 go 垃圾回收变更

Go 语言的垃圾回收器(GC)自其诞生以来一直在不断演进和优化,以提高性能、减少暂停时间和对程序执行的影响。以下是一些关键的改进和变更点:

  1. 并发标记周期

    • Go 语言从一开始就采用了并发标记(Concurrent Marking)的垃圾回收策略,在不影响应用程序主要逻辑的同时进行垃圾回收。
  2. 写屏障

    • 在早期版本中,为了支持并发标记,Go 引入了写屏障技术来追踪在标记过程中可能发生的指针变动。
    • 后续对写屏障进行了优化,例如引入了混合写屏障(Hybrid Write Barrier),减少了写屏障带来的开销。
  3. 并发三色标记

    • Go 1.5 引入了并发三色标记算法(Concurrent Tri-color Marking),进一步缩短了 GC 暂停时间。
  4. 并发清扫与辅助清扫

    • Go 1.8 开始尝试并发清扫阶段,并在后续版本中逐步完善这一机制,使得整个清理过程也可以与应用逻辑并发执行。
    • 辅助清扫(Assisted Sweep)是在特定条件下让工作线程协助完成清扫任务,从而加快清扫速度。
  5. 并行标记

    • Go 1.5 之后还增加了并行标记(Parallel Marking)的支持,通过多核CPU并行化处理标记阶段的工作。
  6. 栈收缩与栈拷贝

    • Go 的垃圾回收还包括对 goroutine 栈的管理,如栈收缩功能可以回收未使用的栈空间。
    • 在某些版本中,对于增长过大的栈会采用栈拷贝的方式来动态调整栈大小。
  7. 抢占式标记

    • Go 1.14 引入了抢占式标记(Preemptive Marking),目的是将长时间运行的 goroutine 对垃圾回收暂停时间的影响降到最低。

每个新版本的 Go 都致力于提升垃圾回收器的效率和对用户代码透明性,确保高并发、高性能的应用场景下能够更好地运行。随着Go社区和开发团队的努力,垃圾回收器的性能表现得到了持续改善。

golang垃圾回收图解 - 知乎

2  如何避免缓存穿透

缓存穿透是指在高并发场景下,查询请求针对的是数据库中不存在的数据(或已失效的缓存数据),由于缓存系统本身的设计是当缓存未命中时从后端存储(如数据库)加载数据并回填到缓存中,但如果数据库也查不到该数据,则不会将空值写入缓存。这样,每次对同一不存在数据发起查询时,都会直接绕过缓存去查询数据库,导致大量无效请求直接打到数据库上,增加了数据库的压力,同时也浪费了网络资源和计算资源。

这种现象称为“缓存穿透”,它会严重影响系统的性能,并可能导致数据库崩溃。解决缓存穿透的主要策略有:

  1. 缓存空对象

    • 对于查询结果为null的Key,仍然将其设置为缓存项,不过期时间可以设置得相对短一些(比如几分钟),防止攻击者持续用同样的非法key频繁击穿缓存。
  2. 布隆过滤器(Bloom Filter)

    • 在访问缓存之前,先通过布隆过滤器进行校验。布隆过滤器是一个空间效率极高的概率型数据结构,它可以用来判断一个给定的key是否可能存在。如果布隆过滤器认为某个key一定不存在,则直接返回,从而避免了对数据库的查询。
  3. 白名单或热点数据预加载

    • 针对某些特定业务场景,可以提前将合法的、可能会被大量查询的ID列表或者范围内的数据对应的键加载到缓存中,形成一种变相的白名单机制。
  4. 使用分布式锁

    • 当一个Key在缓存中未找到且需要查询数据库时,加分布式锁来确保同一时刻只有一个线程去数据库查询,并把查询结果(包括不存在的情况)放入缓存中。
  5. 服务层拦截与校验

    • 在服务层对可能引发穿透的非法请求进行拦截和校验,例如对用户输入的合法性进行严格限制和验证,减少不合理的请求到达数据库。

通过这些策略,可以有效地降低甚至避免缓存穿透的发生,保护后端数据库免受恶意攻击或者高并发下无效请求的冲击。

3 Golang GC 发展历史,版本号,优点

Go(Golang)语言的垃圾回收器(GC)在发展过程中不断进行优化和改进,以适应更高的性能需求和多核处理器环境。以下是其发展历史、重要版本及对应优点概述:

  1. 早期版本

    • Go 1.0 (2012年):初始GC实现基于标记-清扫算法,并采用Stop-The-World (STW) 策略,确保在垃圾回收期间程序暂停执行。
  2. 并发标记与清扫

    • Go 1.5 (2015年):引入了并发标记阶段,使得GC可以在应用程序运行时并发地进行部分工作,显著减少了STW时间。
  3. 写屏障与辅助清扫

    • 后续版本中,为了解决并发标记期间的指针更新问题,GC引入了写屏障技术。同时开始利用空闲的工作线程进行辅助清扫,进一步减少STW时间。
  4. 抢占式标记

    • Go 1.14 (2020年):新增了抢占式标记功能,允许GC在长时运行的goroutine上适时插入标记工作,从而降低最坏情况下的停顿时间。
  5. 持续优化

    • 各个版本迭代中,Go团队不断对GC进行优化,包括:
      • 提高内存分配效率和回收速度。
      • 减少GC暂停时间和提高可预测性。
      • 优化对大内存分配和释放的支持。
  6. 优点

    • 自动内存管理:Go的GC自动处理内存分配和回收,降低了程序员手动管理内存的复杂性和风险。
    • 并发处理:GC支持并发标记,能够更好地利用多核CPU资源,减小对应用响应时间和吞吐量的影响。
    • 可预测性:通过不断优化,Go GC努力提供更短且可预测的暂停时间,尤其对于实时性要求较高的服务而言极为重要。

总之,Go的GC从最初的简单实现逐步演变为一个高度并发且具有较强适应性的垃圾回收系统,其目标是为开发者提供一个高效、稳定且易于使用的内存管理方案。随着每次版本更新,Go团队都在不断地针对GC进行改进,力求在高性能和低延迟之间取得平衡。

Go 的垃圾回收机制在实践中有哪些需要注意的地方? - 知乎

4 并发调度模型GMP

并发调度模型GMP(Goroutine-Monitor-Processor)并非Go语言官方的术语,但这种表述可能是指代早期对Go语言并发调度机制的一种简化描述。实际上,Go语言的并发调度模型核心组件是Goroutines、线程(OS Threads或称为M:Machine)和处理器P(Processor),而非GMP。

Go语言中的并发调度机制如下:

  1. Goroutines

    • Goroutines是Go语言中的轻量级线程,由Go运行时自动管理。它们可以快速创建和销毁,并且可以在多个操作系统线程之间进行上下文切换,实现高并发执行。
  2. 线程(M / Machine)

    • M代表与操作系统交互的实际线程,每个M都绑定到一个操作系统线程上。
    • M负责从处理器P那里获取并执行goroutine。
  3. 处理器(P / Processor)

    • P表示逻辑处理器,它维护着一个工作队列,其中包含待执行的goroutine。
    • 每个P都有一个本地的工作队列以及内存分配缓存,用于提高内存分配效率。
    • Go程序启动时会创建一定数量的P,这个数量可以通过环境变量GOMAXPROCS来设置,从而控制并发执行的最大任务数。
  4. 调度过程

    • 当创建一个新的goroutine时,它会被添加到全局队列或某个P的本地队列中。
    • M需要与P结合才能执行goroutine,M空闲时会尝试从全局队列或其它P的本地队列中“窃取”任务来执行。
    • 在垃圾回收(GC)期间,所有活动的M都会暂停执行,等待GC完成后再继续。

通过上述设计,Go语言实现了高效的并发编程,使得开发者无需关注底层线程管理和同步细节,就能轻松地编写出高性能的并发程序。

5 go 静态属性

在Go语言中,静态属性(通常称为全局变量或包级变量)是指在包作用域内定义的变量,它们在整个程序运行期间都存在,并且对当前包内的所有函数都是可见的。这些变量在内存中的生命周期从程序启动开始到程序结束,存储在程序的数据段或BSS段,而非函数调用栈上。

例如:

 

Go

1package main
2
3// 静态属性(全局变量)
4var GlobalVar int = 100
5
6func main() {
7    // 在这个函数中可以直接访问GlobalVar
8    println(GlobalVar)
9    
10    // 可以修改全局变量
11    GlobalVar = 200
12}
13
14// 其他在同一包内的函数也可以直接访问和修改此全局变量
15func SomeFunction() {
16    println(GlobalVar)
17}

需要注意的是,在并发编程中,多个goroutine同时读写全局变量可能会引发竞态条件,因此通常推荐使用互斥锁或其他同步机制来保护全局变量的安全访问。此外,过度依赖全局变量可能降低代码的可读性和可维护性,建议尽量减少全局变量的使用并遵循最小权限原则。

6 go 什么情况下出现逃逸

在Go语言中,变量逃逸(Escape Analysis)是指编译器分析程序中的局部变量是否能够在函数栈上分配内存,或者必须分配到堆上。如果一个局部变量满足以下条件之一,则可能逃逸:

  1. 返回局部变量的地址
    • 当函数返回一个指向局部变量的指针时,该局部变量需要保证在其生命周期外仍然可访问,因此它必须分配在堆上。
 

Go

1func createInt() *int {
2    i := new(int) // 或者 var i int; i = 0
3    *i = 42
4    return i
5}
  1. 全局或包级别变量引用了局部变量
    • 如果一个局部变量被全局或包级别的变量引用,那么这个局部变量可能会逃逸到堆上,以确保即使函数执行结束,这个局部变量依然可用。
 

Go

1var globalVar *int
2
3func foo() {
4    local := 10
5    globalVar = &local // 这将导致 local 可能会逃逸到堆上
6}
  1. 闭包引用了局部变量
    • 当一个闭包捕获并引用了其外部函数作用域内的局部变量时,为了保证闭包可以在其外部函数执行完毕后继续使用这些变量,这些局部变量会被复制或移动到堆上。
 

Go

1func createClosure() func() int {
2    var local int = 10
3    return func() int {
4        return local
5    }
6}
  1. 切片、映射等内部结构动态增长

    • 对于初始化时容量较小但可能在函数内扩容的切片、映射等数据结构,它们的内部元素可能会因为扩容而分配到堆上。
  2. 并发编程

    • 在并发环境中,如果多个goroutine共享访问一个局部变量,为了防止竞争条件和数据不一致,编译器可能会决定将该变量移到堆上。

总的来说,当局部变量的生命周期超出其所在函数的作用范围,或者因其他原因需要长期存在时,就可能发生逃逸,转而在堆上分配内存。

7 make 与 new

在Go语言中,makenew都是用于分配内存的内置函数,但它们之间存在一些关键的区别:

  1. new()
    • new()函数主要用来为指定类型的值分配零值初始化的内存空间,并返回指向新分配内存的指针。
    • 使用方式:var p *T = new(T),其中T是类型名,返回的是类型T的一个未初始化(零值)实例的指针。

例如:

 

Go

1type MyType struct {
2    a int
3    b string
4}
5
6func main() {
7    var x *MyType = new(MyType)
8    fmt.Println(x) // 输出:&{0 ""}
9}
  1. make()
    • make()函数主要用于内建类型如切片(slice)、映射(map)以及通道(channel)的初始化。对于这些类型,直接使用new()并不能创建一个有效的实例,需要通过make()来完成。
    • make()不仅分配内存,还会对数据结构进行初始化,使其可以立即使用。

例如:

 

Go

1func main() {
2    // 创建一个整数切片
3    slice := make([]int, 5) // 创建长度为5的切片,初始元素都为零值
4    fmt.Println(slice) // 输出:[0 0 0 0 0]
5
6    // 创建一个映射
7    mapExample := make(map[string]int)
8    mapExample["one"] = 1
9    fmt.Println(mapExample) // 输出:map[one:1]
10
11    // 创建一个无缓冲的通道
12    channel := make(chan int)
13    go func() { channel <- 1 }()
14    value := <-channel
15    fmt.Println(value) // 输出:1
16}

总结:

  • 如果要创建基本类型或自定义类型的指针并对其进行初始化,请使用new()
  • 对于切片、映射和通道这类内建类型,必须使用make()来创建和初始化。

相关文章:

go垃圾回收

1 go 垃圾回收变更 Go 语言的垃圾回收器&#xff08;GC&#xff09;自其诞生以来一直在不断演进和优化&#xff0c;以提高性能、减少暂停时间和对程序执行的影响。以下是一些关键的改进和变更点&#xff1a; 并发标记周期&#xff1a; Go 语言从一开始就采用了并发标记&#xf…...

如何做代币分析:以 LEO 币为例

作者&#xff1a; lesleyfootprint.network 编译&#xff1a;cicifootprint.network 数据源&#xff1a;LEO 代币仪表板 &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关的数…...

数制和码制

目录 几种常见的数制 数制 基数 位权 常见的四种数制 十进制数 二进制数 八进制数 十六进制数 不同进制数的相互转换 例如 例如 编码 二-十进制码 例如 格雷码 例如 原码、反码和补码 几种常见的数制 关键术语 数制&#xff1a;以一组固定的符号和统一的规则来表示数值…...

Git Bash中安装tree

文章目录 问题描述解决办法A备选办法BRef 问题描述 在Git Bash中使用tree报错&#xff1a; tree # bash: tree: command not found解决办法A 下载二进制文件&#xff1a; https://gnuwin32.sourceforge.net/packages/tree.htm -> 选binary。下载后解压.zip 把解压后的tre…...

java开源 VR全景商城 saas商城 b2b2c商城 o2o商城 积分商城 秒杀商城 拼团商城 分销商城 短视频商城 小程序商城搭建

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…...

C++练手题系列一

第 1 题 【 问答题 】 • 有多少种二叉树 输入n(1<n<13)&#xff0c;求n个结点的二叉树有多少种形态 时间限制&#xff1a;1000 内存限制&#xff1a;65536 输入 整数n 输出答案 样例输入 3 样例输出 5 第 2 题 【 问答题 】 • 城堡问题 1 2 3 4 5 6 7 ################…...

(下)async/await高级用法,你会多少种呢?

公众号&#xff1a;程序员白特&#xff0c;欢迎一起交流学习~ 前言 众所周知&#xff0c;在 ECMA17 中加入了两个关键字 async、await&#xff0c;简单来说它们是基于 Promise 之上的语法糖&#xff0c;可以让异步操作更加简洁明了。在掌握async、await的基础用法后&#xff0…...

阅读笔记 | REFORMER: THE EFFICIENT TRANSFORMER

阅读论文&#xff1a; Kitaev, Nikita, Łukasz Kaiser, and Anselm Levskaya. “Reformer: The efficient transformer.” arXiv preprint arXiv:2001.04451 (2020). 背景与动机 这篇论文发表较早&#xff0c;主要关注Transformer的效率问题。标准的Transformer模型在许多自然…...

光路科技:工业以太网交换机引领工业互联网新篇章

随着全球范围内工业4.0的浪潮不断涌动&#xff0c;工业互联网作为其核心驱动力&#xff0c;正引领着工业生产向智能化、网络化的崭新阶段迈进。在这一转型的浪潮中&#xff0c;光路科技凭借其卓越的工业互联设备与创新解决方案&#xff0c;正为工业互联网领域的发展注入新的活力…...

航拍无人机技术,航拍无人机方案详解,无人机摄影技术

航拍无人机是利用遥控技术和摄像设备&#xff0c;在空中进行拍摄和录像的无人机。这种无人机通常具有高清摄像设备、图像传输设备、GPS定位系统、智能控制系统等&#xff0c;可以轻松实现各种拍摄角度和高度&#xff0c;广泛应用于影视制作、旅游景区航拍、城市规划、环保监测等…...

【ArcPy】游标访问几何数据

访问质心坐标相关数据 结果展示 代码 import arcpy shppath r"C:\Users\admin\Desktop\excelfile\a2.shp" with arcpy.da.SearchCursor(shppath, ["SHAPE","SHAPEXY","SHAPETRUECENTROID","SHAPEX","SHAPEY",&q…...

Java云计算k8s

云计算k8s k8s简介容器技术的发展使用kubeadm安装k8skubectlKubernetes 架构k8s节点节点与控制面之间的通信控制器k8s kubectl命令详解k8s容器Kubernetes 对象Kubernetes 对象管理Kubernetes 对象管理 指令式命令Kubernetes 对象管理 指令式对象配置k8s对象管理 声明式对象配置…...

蜂窝物联:物联网大数据云平台功能模块简介

蜂窝云平台可远程获取现场环境&#xff08;如温室大棚、稻田&#xff09;的空气温湿度、土壤水分温度、二氧化碳浓度、光照强度及视频图像&#xff0c;通过数据模型分析&#xff0c;可以自动控制湿帘、风机、喷淋滴灌、内外遮阳、顶窗侧窗、加温补光、增氧机等设备&#xff1b;…...

Docker镜像导出/导入

Docker镜像导出/导入 一、前言 在实际操作中&#xff0c;为了便于docker镜像环境和服务配置的迁移&#xff0c;我们有时需要将已在测试环境主机上完成一系列配置的docker镜像或运行中的容器镜像导出&#xff0c;并传输到生产或其他目标环境主机上运行。为此&#xff0c;本文主…...

SwiftUI 如何在运行时从底层动态获取任何 NSObject 对象实例

概览 众所周知,SwiftUI 的推出极大地方便了我们这些秃头码农们搭建 App 界面。不过,有时我们仍然需要和底层的 UIKit 对象打交道。比如:用 SwiftUI 未暴露出对象的接口改变视图的行为或外观。 从上图可以看到,我们 SwiftUI 代码并没有设置视图的背景色,那么它是怎么变成绿…...

LeetCode 2161.根据给定数字划分数组

给你一个下标从 0 开始的整数数组 nums 和一个整数 pivot 。请你将 nums 重新排列&#xff0c;使得以下条件均成立&#xff1a; 所有小于 pivot 的元素都出现在所有大于 pivot 的元素 之前 。 所有等于 pivot 的元素都出现在小于和大于 pivot 的元素 中间 。 小于 pivot 的元素…...

ip获取+归属地实现

1.背景 现在的社交平台一般都需要展示用户的归属地,这个功能有下面二个主要功能点,接下来我们来介绍下具体实现。 IP 获取 IP 转归属地 2.ip获取 2.1 Http请求 对于controller的请求,我们只需要写个拦截器,将用户的ip设置进上下文即可,非常方便。 @Override public bo…...

Python的错误和异常

文章目录 python的语法错误异常异常处理用户自定义异常定义清理行为预定义的清理行为 python的语法错误 语法错误&#xff08;Syntax Error&#xff09;是指代码不符合Python语言的语法规则。当解释器在执行代码之前对其进行解析时&#xff0c;如果发现代码中有语法错误&#…...

C语言-------指针进阶(2)

1.指针数组 指针数组表较简单&#xff0c;类比整型数组&#xff0c;字符数组&#xff0c;整型数组里面的元素都是整型变量&#xff0c;字符数组里面 的元素是字符类型&#xff0c;那么指针数组就是数组里面的每个元素都是指针类型&#xff0c;例如int*arr[5]就是一个 指针数…...

Spring El表达式官方文档学习

文章目录 推荐一、概述1、什么是SpEL2、SpEL能做什么 二、SpEL表达式使用1、文字表达式2、属性, 数组, List, Map,和 索引&#xff08;1&#xff09;属性操作&#xff08;2&#xff09;数组和List&#xff08;3&#xff09;Map 3、内嵌List4、内嵌Map5、构建数组6、调用类的方法…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

【Java_EE】Spring MVC

目录 Spring Web MVC ​编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 ​编辑参数重命名 RequestParam ​编辑​编辑传递集合 RequestParam 传递JSON数据 ​编辑RequestBody ​…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

2025季度云服务器排行榜

在全球云服务器市场&#xff0c;各厂商的排名和地位并非一成不变&#xff0c;而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势&#xff0c;对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析&#xff1a; 一、全球“三巨头”…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)

考察一般的三次多项式&#xff0c;以r为参数&#xff1a; p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]&#xff1b; 此多项式的根为&#xff1a; 尽管看起来这个多项式是特殊的&#xff0c;其实一般的三次多项式都是可以通过线性变换化为这个形式…...

【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制

使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下&#xff0c;限制某个 IP 的访问频率是非常重要的&#xff0c;可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案&#xff0c;使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

比较数据迁移后MySQL数据库和OceanBase数据仓库中的表

设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...