深入理解golang 的栈

线程栈(thread stacks)介绍

先回顾下linux的内存空间布局


简书_stack02.png

当启动一个C实现的thread时,C标准库会负责分配一块内存作为这个线程的栈。标准库分配这块内存,告诉内核它的位置并让内核处理这个线程 的执行。
在linux系统中,可通过 ulimit -s查看系统栈大小(8M)。
ulimit -s 10240可修改栈大小为10M。

 

这里最大的一个问题是,分配大数组,或者循环递归函数时,默认的栈空间不够用,会导致Segmentation fault错误。

//testMaxStack.cpp
#include <stdio.h> int main() { printf("init ok\n"); char a[8192*1024]; // 8M空间 printf("run over\n"); } //执行结果 [app@VM_114_13_centos c]$ ulimit -s 8192 [app@VM_114_13_centos c]$ g++ testMaxStack.cpp [app@VM_114_13_centos c]$ ./a.out Segmentation fault 

解决方法有两个:

  • ulimit -s 10240调整标准库给所有线程栈分配的内存块的大小。但是全线提高栈大小意味着每个线程都会提高栈的内存使用量,这样一来,你将用光所有内存。
  • 为每个线程单独确定栈大小。这样一来你就不得不完成这样的任务:根据每个线程的需要,估算它们的栈内存的大小。这将是创建线程的难度超出我们的期望。

Go是如何应对这个问题的

Go使用的解决方案类似第二种方法。
goroutine 初始时只给栈分配很小的空间,然后随着使用过程中的需要自动地增长。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。
Go 1.4开始使用的是连续栈,而这之前使用的分段栈

分段栈(Segmented Stacks)

分段栈(segmented stacks)是Go语言最初用来处理栈的方案。
当创建一个goroutine时,Go运行时会分配一段8K字节的内存用于栈供goroutine运行使 用。

每个go函数在函数入口处都会有一小段代码,这段代码会检查是否用光了已分配的栈空间,如果用光了,这段代码会调用morestack函数。

morestack函数

morestack函数会分配一段新内存用作栈空间,接下来它会将有关栈的各种数据信息写入栈底的一个struct中(下图中Stack info),包括上一段栈的地址。然后重启goroutine,从导致栈空间用光的那个函数(下图中的Foobar)开始执行。这就是所谓的“栈分裂 (stack split)”。

  +---------------+|               ||   unused      ||   stack       || space | +---------------+ | Foobar | | | +---------------+ | | | lessstack | +---------------+ | Stack info | | |-----+ +---------------+ | | | +---------------+ | | Foobar | | | | <---+ +---------------+ | rest of stack | | | 
lessstack函数

在新栈的底部,插入了一个栈入口函数lessstack。设置这个函数用于从那个导致我们用光栈空间的函数(Foobar)返回时用的。当那个函数(Foobar)返回时,我们回到lessstack(这个栈帧),lessstack会查找 stack底部的那个struct,并调整栈指针(stack pointer),使得我们返回到前一段栈空间。这样做之后,我们就可以将这个新栈段(stack segment)释放掉,并继续执行我们的程序了。

分段栈的问题

栈缩小是一个相对代价高昂的操作。如果在一个循环中调用的函数遇到栈分裂 (stack split),进入函数时会增加栈空间(morestack 函数),返回并释放栈段(lessstack 函数)。性能方面开销很大。

连续栈(continuous stacks)

go现在使用的是这套解决方案。
goroutine在栈上运行着,当用光栈空间,它遇到与旧方案中相同的栈溢出检查。但是与旧方案采用的保留一个返 回前一段栈的link不同,新方案创建一个两倍于原stack大小的新stack,并将旧栈拷贝到其中
这意味着当栈实际使用的空间缩小为原先的 大小时,go运行时不用做任何事情。
栈缩小是一个无任何代价的操作(栈的收缩是垃圾回收的过程中实现的.当检测到栈只使用了不到1/4时,栈缩小为原来的1/2)。
此外,当栈再次增长时,运行时也无需做任何事情,我们只需要重用之前分配的空闲空间即可。

如何捕获到函数的栈空间不足

Go语言和C不同,不是使用栈指针寄存器和栈基址寄存器确定函数的栈的。

在Go的运行时库中,每个goroutine对应一个结构体G,大致相当于进程控制块的概念。这个结构体中存了stackbasestackguard,用于确定这个goroutine使用的栈空间信息。每个Go函数调用的前几条指令,先比较栈指针寄存器跟g->stackguard,检测是否发生栈溢出。如果栈指针寄存器值超越了stackguard就需要扩展栈空间。

旧栈数据复制到新栈

旧栈数据复制到新栈的过程,要考虑指针失效问题。
Go实现了精确的垃圾回收,运行时知道每一块内存对应的对象的类型信息。在复制之后,会进行指针的调整。具体做法是,对当前栈帧之前的每一个栈帧,对其中的每一个指针,检测指针指向的地址,如果指向地址是落在旧栈范围内的,则将它加上一个偏移使它指向新栈的相应地址。这个偏移值等于新栈基地址减旧栈基地址


链接:https://www.jianshu.com/p/7ec9acca6480

转载于:https://www.cnblogs.com/mafeng/p/10305419.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/463607.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

const和define的区别

今天查看以前的代码&#xff0c;进行优化&#xff0c;回想到const和define一些区别&#xff0c;记录下来。 1.const是关键字&#xff0c;define不是关键字。 2.const定义的是只读变量&#xff0c;不是常量&#xff0c;define宏定义的是常量&#xff0c;变量不能作为定义数组的维…

理解ALSA

最近处理音频的问题&#xff0c;所以看了一些不错的文章&#xff0c;整理一些有用的资料出来&#xff0c;有需要的可以收藏。ALSA的框架图&#xff1a;这个图可以说是我目前看到最不错的&#xff0c;我发现很多应用开发的&#xff0c;一出现解决不了的问题&#xff0c;或者奇怪…

Xshell 6如何设置多个session显示在同一个窗口

刚才安装了Xshell 6之后&#xff0c;发现在同一个窗口只能显示4个session&#xff0c;网上查找了一些资料但是都不是想要的结果&#xff0c;经过几分钟的查找&#xff0c;终于找到了设置在同一个窗口session的个数&#xff0c;因此记录下来&#xff0c;或者给与他人帮助。以下以…

appium+java(五)微信小程序自动化测试实践

前言&#xff1a; 上一篇《appiumjava&#xff08;四&#xff09;微信公众号自动化测试实践》中&#xff0c;尝试使用appium实现微信公众号自动化测试&#xff0c;接着尝试小程序自动化&#xff0c;以学院小程序为例 准备工作 1、java-client 3.4.16依赖包 2、微信应用版本7.0.…

blockUI应用到Asp.Net页面时服务器控件(Button等)失效的问题

问题&#xff1a;在Asp.Net页面中用blockUI这个控件实现弹出窗口的效果&#xff0c;弹出页面内容为页面中某个Panel中的内容&#xff0c;包含TextBox、Button等服务器控件。使用时就简单的设置message属性。问题出来了&#xff0c;当显示这个弹出页面后&#xff0c;所有Button等…

android DatePicker

为什么80%的码农都做不了架构师&#xff1f;>>> public class DatePicker extends FrameLayout java.lang.Object android.view.View android.view.ViewGroup android.widget.FrameLayout android.widget.DatePicker DatePicker 一个选择年月日的日历布局视图 公…

一次限制进程的 CPU 用量的实操过程

大家好&#xff0c;我是飞哥&#xff01;给大家分享一个事情。背景是这样的&#xff0c;我们要测试某个第三方 SDK 运行性能&#xff0c;这是个 CPU 密集型的服务。我想评估一下它运行一遍到底有多吃 CPU&#xff0c;以便评估上线后我们需要部署多少台服务器。我们是在一台 16 …

map与unordered_map的区别

set/map底层实现的机制是红黑树。红黑树是一种近似于平衡的二叉查找树&#xff0c;默认是按升序排序的。在红黑树上做查找、插入、删除操作的时间复杂度为O(logN)。 红黑树的缺点&#xff1a;空间占用率高&#xff0c;每一个节点都需要额外保存父节点、孩子节点和红/黑性质&am…

navicat不同数据库数据传输

复制fo的t_fo_account表结构和数据到base库 结果 转载于:https://www.cnblogs.com/feifeicui/p/10307646.html

Win2003 IIS下,ASP.NET无法访问数据库和网页

1.Win2003 IIS下,ASP.NET无法访问网页 将IIS的 允许ASP 设置为允许. 2.Win2003 IIS下,ASP.NET无法访问数据库(这里我用的是Oracle9i) 1)将网站的虚拟目录 添加 ASP.NET 和 NETWORK_SERVICE用户. 2)oracle目录下ora92目录的Authenticateduser用户 去掉勾中的权限 再勾上权限. 最…

FTP自动上传日期命名文件

说明&#xff1a;此文章是从http://177048.blog.51cto.com/167048/919374转载过来的&#xff0c;若有侵权之处&#xff0c;请联系本人&#xff0c;及时删除&#xff0c;谢谢&#xff01; 需求&#xff1a;将每天备份的数据以当天日期命名&#xff0c;并定时上传到FTP服务器上。…

收藏了两年的嵌入式AI资源学习笔记,今天全分享给大家(附代码/资料/视频/学习规划)...

当前乃至未来5-10年&#xff0c;嵌入式开发者还有哪些风口&#xff1f;”画外音&#xff1a;风口的本质&#xff0c;其实就是一段时间的人才供需不平衡。说白了就是由于行业突变&#xff0c;敏锐的资本快速进入&#xff0c;导致短时间内行业大量扩张&#xff0c;需要大量开发者…

gcc -O0 -O1 -O2 -O3 四级优化选项及每级分别做什么优化

今天看到了一篇文章&#xff0c;写的挺好就将其转载&#xff0c; https://blog.csdn.net/zhangzq86/article/details/80840927 Gcc 编译优化简介 gcc 提供了为了满足用户不同程度的的优化需要&#xff0c;提供了近百种优化选项&#xff0c;用来对{编译时间&#xff0c;目标文…

Vmware由于centos升级内核不可运行(C header files matching your running kernel were not found)的解决方案...

C header files matching your running kernel were not found. Refer to your distributions documentation for installation instructions - NoH4cker - 博客园 http://www.cnblogs.com/NoH4cker/p/4840571.html centos6 安装wmwaretools找不到kernel header - jiejnan - 博…

分享一个消息组件

前段时间在收集项目素材时发现一个很好用的消息组件ymPrompt,顺便收集了圈子里关于这个组件的文章&#xff0c;感觉介绍不是很完善。 废话少说先看一下演示效果: 演示Demo: http://www.ajaxbbs.net/test/ymPrompt4.0/demo.html 截取的图片: Vista样式 简短的实现脚本: Code--导…

用C语言搞机器学习,来个最基础的Knn入门

本来是准备周末加班两天的&#xff0c;然后&#xff0c;临时突然其他事情又取消了。顺便看了下csdn&#xff0c;看到一篇介绍KNN的&#xff0c;因为我现在做的也是属于机器学习方向&#xff0c;那自然也要了解一些这部分。KNN是什么&#xff1f;KNN可以说是最简单的分类算法之一…

如何解决padding标记在ie7、ie6以及firefox中的兼容问题

*html 与 *html 是IE特有的标签, firefox 暂不支持.而*html 又为 IE7特有标签。所以要解决padding的兼容问题就要靠前面提到的标签。 以sccas-site为例&#xff0c;左侧导航栏在padding上产生了ie6、ie7以及ff浏览器不兼容&#xff0c;修改代码如下&#xff1a; #menu7 li a {h…

linux编译动态库之fPIC

转载&#xff1a;https://blog.csdn.net/sinc00/article/details/44833839 今天在用g编译代码时&#xff0c;提示说.rdata错误&#xff0c;然后网上找了一堆资料&#xff0c;最后明白了一个要重新编译对应的链接库。 在生成动态库时&#xff0c;常常习惯性的加上fPIC选项&…

每周一题 扫雷问题

扫雷问题 #include<iostream> #include<vector> #include<iostream> using namespace std; int direct[8][2]{ {1,0}, {1,1}, {1,-1}, {0,1}, {0,-1}, {-1,0}, {-1,1}, {-1,-1} }; int main(){int m, n, t0;while(1){cin>>m>>n;if(m0&&n…

Oracle学习笔记:通过种子数据库设置dbid为指定值

简介&#xff1a;dbms_backup_restore包真是太强大了。和设置dbid有关的存储过程如下&#xff1a; PROCEDURE nidbegin  (newdbname IN varchar2,       olddbname IN varchar2,       newdbid IN number, …