记录一次golang中关于值传递和引用传递,内存逃逸的学习笔记

news/2024/12/15 22:02:56/

起因:最近在翻看代码时,发现有的同时在使用golang gorm库查询单条数据时,和官方文档中有点区别:

同事的代码大致长这样:

golang">// 依据商品id查询商品详情
func (m GoodsModel) FindOneById(id uint32, field string) (*Goods, error) {info := &Goods{}db := mysqldriver.GetDB()err := db.Table(m.TableName()).Select(field).Where("id = ?", id).First(info).Errorif err != nil && err != gorm.ErrRecordNotFound {return info, err}return info, nil
}

如果这段代码是我来写大致长这样:

golang">// 依据商品id查询商品详情
func (m GoodsModel) FindOneById(id uint32, field string) (Goods, error) {info := Goods{}db := mysqldriver.GetDB()err := db.Table(m.TableName()).Select(field).Where("id = ?", id).First(&info).Errorif err != nil && err != gorm.ErrRecordNotFound {return info, err}return info, nil
}

gorm官方文档中给出的案例:

在这里插入图片描述

在我们的逻辑层是创建一个新的变量来接收FindOneById的返回值
大致长这样:

golang">var res GooodsInfoResponse// 上面巴拉巴拉一堆逻辑// 查询sdb_goods商品信息
goodsInfo, _ := NewGoodsModel.FindOneById(31415, "id,name")		// 下面也是巴拉巴拉一堆逻辑
// 但是在我们的逻辑层,并不会把goodsInfo返回出去
// 而是获取goodsInfo的值赋值到新的返回结构体中res.Name = goodsInfo.Namereturn res, nil

所以我的疑问点就在于, 同事为什么会在FindOneById时将变量的指针地址返回出去
就我目前的了解,这会导致内存逃逸

重点来了
我所理解的相关知识

1.栈和堆的内存管理机制与变量的生命周期密切相关。简单来说,栈上分配的内存相对较小、生命周期较短,而堆上分配的内存较大、生命周期较长。Go 的垃圾回收器(GC)负责管理堆内存,自动回收不再使用的对象

2.逃逸分析决定了一个变量是否需要分配到堆上,主要看变量的生命周期。具体来说,如果变量 逃出了它所在的函数或者作用域(即超出了栈的作用范围),Go 会将其分配到堆上。

golang">// 由于我们返回了 a 的地址,a 会“逃逸”到堆上,因为它需要在 bar 函数外部存活
func bar() *int {a := 10      // 变量a分配在栈上return &a    // 返回a的地址,导致a逃逸到堆上
}

然后我同事的解释有两点,我总结如下

1.指针地址是因为我们的变量有可能很大,在变量很大的情况下有可能会被自动分配到堆,所以返回指针地址

这一点据我了解,逃逸分析的核心是生命周期,而不是变量的大小。如果一个变量在函数外部仍然需要使用(比如返回值或者跨函数传递),那么它会被分配到堆上,不论它的大小如何。

2.在使用gorm中First方法时,本身就是将指针地址传递进去,干脆就直接申明指针地址,而不是直接申明变量,因为将栈上的变量指针地址传递给另一个方法时,会将栈上的变量重新分配到堆上.

这一点要懂哥来给我科普一下gorm包内部有没有什么知识点

至于 栈上的变量指针地址传递给另一个方法时,会将栈上的变量重新分配到堆上 这一点,我敢肯定的是,不会;这很明显是在狡辩,本来分配在栈上的变量怎么可能因为你获取变量的指针地址而发生逃逸呢?
获取 &a(即取地址)并不会立即将变量移到堆上,除非该地址被传递到外部,导致变量逃逸。获取指针本身通常是一个低开销的操作。

栈和堆的性能差异
栈上的内存访问速度较快,适合短生命周期的局部变量。
堆上的内存需要通过指针访问,访问速度较慢,且涉及到垃圾回收等额外开销。


虽然我们的系统跑的好好的,为什么觉得我还是觉得需要修改这段代码,是因为我始终觉得,当一个变量超出了作用域的时候就应该被销毁掉,不能过度依赖语言的垃圾回收机制,GC嘛本来就是一个开销比较大的操作,这里就不得不提Rust的强大了,人家压根就没有垃圾回收机制


http://www.ppmy.cn/news/1555422.html

相关文章

Flink是什么?Flink技术介绍

官方参考资料:Apache Flink — Stateful Computations over Data Streams | Apache Flink Flink是一个分布式流处理和批处理计算框架,具有高性能、容错性和灵活性。以下是关于Flink技术的详细介绍: 一、Flink概述 ‌定义‌:Fli…

MongoDB 分片

MongoDB 分片 MongoDB 分片是一种数据库架构,用于将大量数据分布存储在多个服务器上。这种设计允许数据库扩展,以处理大量数据和高吞吐量操作。分片通过将数据集分割成小块,称为分片,并将这些分片分布到多个服务器上来工作。每个…

Python列表与字典的性能比较:如何选择最适合的数据结构

在Python中,列表(List)和字典(Dictionary)是最常用的数据结构之一。它们都能够有效地存储数据,并提供高效的操作方式,但它们在内部实现、操作复杂度以及应用场景上存在显著的差异。在进行程序设…

docker开启远程访问

1、编辑docker.server文件 vi /usr/lib/systemd/system/docker.service 找到 [Service] 节点,修改 ExecStart 属性,增加 -H tcp://0.0.0.0:2375 ExecStart/usr/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock -H tcp://0.0.0.0:2…

微信小程序5-图片实现点击动作和动态加载同类数据

搜索 微信小程序 “动物觅踪” 观看效果 感谢阅读,初学小白,有错指正。 一、功能描述 a. 原本想通过按钮加载背景图片,来实现一个可以点击的搜索button,但是遇到两个难点,一是按钮大小调整不方便(网上搜索…

【第二节】docker应用系列篇: docker运行单机mysql

系列文章目录 【第一节】docker应用系列篇: docker运行单机mysql 系列文章目录前言一、 docker运行mysql容器二、 客户端访问mysql 前言 提示:以下是本篇文章正文内容,下面案例可供参考 一、 docker运行mysql容器 docker run -p 3306:3306 …

C# 探险之旅:第二十三节 - 字符(char):字符小精灵的独舞

嘿,探险家们!欢迎再次踏上C#王国的奇妙旅程。这一节里,我们要深入探索一个非常基础但又极其重要的角色——字符(char)。想象一下,你正在参加一场由单个字母和数字组成的精灵舞会,每个精灵都代表着一个独特的字符。让我…

内网穿透讲解

什么是内网穿透 内网穿透是一种网络技术,它允许外网或者其他局域网的用户来访问这个局域网的服务器资源,让资源的利用率更高,更加灵活,但是也要确保网络安全。 工作原理 如果你在公司,但是你需要使用到你家里的那台电…