网页资讯视频图片知道文库贴吧地图采购
进入贴吧全吧搜索

 
 
 
日一二三四五六
       
       
       
       
       
       

签到排名:今日本吧第个签到,

本吧因你更精彩,明天继续来努力!

本吧签到人数:0

一键签到
成为超级会员,使用一键签到
一键签到
本月漏签0次!
0
成为超级会员,赠送8张补签卡
如何使用?
点击日历上漏签日期,即可进行补签。
连续签到:天  累计签到:天
0
超级会员单次开通12个月以上,赠送连续签到卡3张
使用连续签到卡
08月10日漏签0天
godot吧 关注:8,918贴子:40,297
  • 看贴

  • 图片

  • 吧主推荐

  • 游戏

  • 18回复贴,共1页
<<返回godot吧
>0< 加载中...

「通俗讲解」Godot类型内存解析

  • 只看楼主
  • 收藏

  • 回复
  • 多能豆
  • 小吧主
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
有一段时间没发帖子了,主要是生活所迫,最近有了时间,并且本人已经学习了一段时间的Godot源码,也算是有所见解,所以准备把Godot类型的内存分布与管理讲一下
顺便插一句,因为之前电脑出了一点问题,我还把工作环境迁移到Linux:

-----------------------------------------分割线-----------------------------------------
正文:
对于Godot类型来说,在gds中,因为官方已经将大部分工作压缩精简,所以gds中我们很少会花心思去管理内存,
所以有时候gds在传递变量时,往往会做出令我们“匪夷所思”的行为,例如那引用传递,在本篇帖子中,我们就来探讨其中的底层原理。
逻辑规划:
1.栈与堆
2.Godot类型内存分类
3.总结与实践


可以看到本帖篇幅不长,所以我们赶快开始吧!


  • seed071608
  • 数学算法
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
快开始


2025-08-10 08:46:54
广告
不感兴趣
开通SVIP免广告
  • 多能豆
  • 小吧主
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
1.栈与堆
注意:为了通俗理解,本人将部分底层原理简化!!!!
在程序运行时,会产生很多数据,例如我们定义的变量,为了储存这些数据,cpu会在内存中开辟一段空间,用于储存数据,而这个空间被叫做“栈”
我们可以将栈看作一列火车,其中的每个数据都是一节车厢,栈中的数据是自上而下分布:

其中,栈底的位置是不变的,而栈顶指向“可用空间的顶端”
举个例子:

当上面这两个全局变量a,b在脚本运行时,内存模型是这样的:

var a=2首先执行,所以a先进入栈,b后进入,栈顶也随之移动。
接下来我们再看局部变量的储存:
同样举个例子:

当_ready执行中:

(这里补充一下,因为绘图演示原因,栈画得很短,看似好像只能存储很少的数据,实际中,栈是可以容纳很多数据的,不要被图片误导)


可以发现,局部变量好像和全局变量在栈中的内存模型相同,的确,所有变量都是这样自上而下地进入栈的,
可是,你有没有想到一个问题:
函数中的变量可以进入栈,但我们可以无限制地调用函数!!!如果这样下去,栈不久会被占满吗?
所以,一般情况下,函数执行结束时,会“清栈”,即将栈顶上移到一开始的样子,可以看做清空局部变量栈:
_ready执行结束后:

可以看到,_ready执行结束后,栈顶上移,La与Lb被“无情”地清除了,
这是一个约定:当函数结束执行后,就会移动栈顶,将局部变量清空!!!
这样就不会存在多次调用函数,栈空间被占满的情况了。
这也说明了:
我们之所以能在各处使用全局变量,是因为它不再任何函数中,所以它对应的栈空间,程序运行时不会被清理,全局变量可以一直存在!

我们之所以不能在指定函数外访问局部变量,是因为局部变量是个“苦命的孩子”,它不仅只在函数执行时才进入堆栈,而且在函数结束后它就会被清理........


  • 多能豆
  • 小吧主
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我们再来看看什么是堆:
简单来说,堆是程序自己申请开辟的内存空间,由程序自己管理,它没有规定的存储方式,所以程序可以随心所欲地使用堆,堆模型如下:

所以我们为什么要用堆呢?
这主要是为了储存一些特殊类型,例如Array:
如果我们把Array放到栈中,因为栈内存是自动管理的,万一Array中的某一个数据被清理了,都会产生意想不到的严重的后果,相比之下把Array放到我们自己的堆中更为保险:

来看一个实际的例子:

上面这段代码运行时,内存模型如下:

可以看到,为了内存安全,实际数组在堆中,因为所有变量都在栈上,所以变量Arr储存了一个“地址”,用于寻找堆中的实际数组
像这种:实际数据在堆中,而栈上的变量储存“地址”的类型,我们就称作“引用类型”,Array,Dictionary,Object(以及派生类)都是引用类型,
这就是堆与栈的知识,下节我们来讲讲Godot中类型的分类。


  • 多能豆
  • 小吧主
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
2.Godot类型内存分类
Godot提供了很多内置类型,这我们分一下类:
1.值类型
2.引用类型
3.次引用类型(其实因该叫“弱引用”,但为了与Java区分,这里称“次引用”)
1.值类型
即这个类型的变量数据,直接就在栈中:
int,float,bool,以及所有数学向量类型(向量,矩阵,包围盒等)都是值类型

而值类型在复制时,是直接在栈中复制:
例如:
var a=1
var b=a
结果:

这时,a,b都是独立的,互不影响。
2.引用类型
即实际数据在堆中,而变量只是储存“地址”
如上节提到的Array:
var Arr=[1,2,3]

引用类型在拷贝时,是将地址复制:
例如:

内存模型:

所以实际上Arr与Arr2指向同一段数据,所以修改其中一个的值,Arr[0]=4,则Arr2[0]也为4
因为它们指向同一数据!!!
所以引用类型在复制时要注意!!!
Array,Dictionary,Object(以及派生类)都是引用类型。
3.次引用类型
这种类型储存与引用类型一致,但拷贝与值类型一致!
例如String:


这储存模型与引用类型一致,
但在拷贝时:


它会将实际数据也拷贝一份,所以这时的S1与S2互不影响!
String和Pool-XXX-Array都属于次引用类型!!!


  • 魔魇风魂
  • 数学算法
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
顶


  • seed071608
  • 数学算法
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
大佬太强了,深入浅出啊


  • 菜园赤子
  • 跨平台
    9
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
tql


2025-08-10 08:40:54
广告
不感兴趣
开通SVIP免广告
  • 多能豆
  • 小吧主
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
3.总结与实践
对于Array与Dictionary储存方式的理解:
小测验一:

上面的代码会输出什么?
我们来分析一下:
首先我们创建了变量S,
然后我们将S“存入”了Arr数组,
接着我们去除了S的第一个字符,
输出结果应该是: str
我们来看一下内存模型:
当执行var S="str"时,S入栈,
当执行var Arr=[S]时,实际上等价于:
var Arr
Arr[0]=S
Arr是一个“地址”,指向堆中的实际数组,而实际数组的第一个元素是拷贝自变量S的!!
变量S是String类型,由上节可知,它是次引用类型,实际数据也会被拷贝,
Arr[0]=S
所以数据 "str" 被拷贝了一份,Arr[0]储存一个“地址”,指向拷贝来的数据!!

由此可知:
Array和Dictionary储存数据时,都是去拷贝原数据!!!!

在这个实例中,Arr[0]拷贝自S,S是String类型,次引用类型,所以Arr[0]与S的数据不会影响
Arr[1]拷贝自常量1,Arr[1]数据是独立的,不会与其它变量影响
Arr[2]拷贝自W,W是Dictionary类型,引用类型,拷贝后Arr[2]与W都是指向堆中同一个实际数据的“地址”,所以会互相影响!
小测验二:

上面的代码会输出什么?
我们来分析一下:
首先我们创建了变量S,
然后我们将S“存入”了Arr数组,
接着我们获取数组的第一个数据,
然后我们将这个数据(实际上就是字符串)去除第一个字符,
实际上,上面的代码会输出 str
为什么呢?
为了严谨,我先把底层实际原因说一下:
因为底层需要,Array和Dictionary中的数据是以Variant类型储存的,而Variant类型可以近似为次引用类型,所以从Arr[0]取出数据时,取出的是实际数据的拷贝
但Variant类型在gds中由程序自动管理,所以上述行径,可以这样通俗理解:
Array和Dictionary为了内部数据安全,在取出数据时,会生成一个临时变量,并执行一次拷贝:

所以从Array和Dictionary取出数据时,实际上取出原数据的拷贝!
这时候就要注意 值类型,引用类型,次引用类型 拷贝时的区别了!!!


  • seed071608
  • 数学算法
    7
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
我以为我懂了,看了测验又懵了。。。。。。


  • LazBug
  • 国际化
    11
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
楼主之前出的研究和谈论都很好,但是这次说实话,最大的问题是,10楼截图给出了一个提问,却没有明确的回答,我想知道答案是什么,却需要把这么多文字全部看明白,如果print的结果能用一个截图显示出来,我们就能一眼看到了,对于一开始不愿意看长文本的吧友们,在长文本里说明结果,可能会把他们搞懵。
这玩意儿说白了就是深拷贝与浅拷贝的区别,S指向“str”字符串,Arr是基于S生成的,其他语言里,很多为了节约内存,会导致我更改了S的“str”信息,Arr也发生改变,但是这里没有,Arr变量是完全拷贝了一份S的信息,即S指向的“str”和Arr指向的“str”不是同一个“str”


登录百度账号

扫二维码下载贴吧客户端

下载贴吧APP
看高清直播、视频!
  • 贴吧页面意见反馈
  • 违规贴吧举报反馈通道
  • 贴吧违规信息处理公示
  • 18回复贴,共1页
<<返回godot吧
分享到:
©2025 Baidu贴吧协议|隐私政策|吧主制度|意见反馈|网络谣言警示