Temp

7805 bookmarks
Custom sorting
The State of the Octoverse | The State of the Octoverse explores a year of change with new deep dives into writing code faster, creating documentation and how we build sustainable communities on GitHub.
The State of the Octoverse | The State of the Octoverse explores a year of change with new deep dives into writing code faster, creating documentation and how we build sustainable communities on GitHub.
In this year’s Octoverse Report, we share research about developing code, creating documentation, and supporting communities so that you can use these insights to create great developer experiences for teams of varied sizes.
·octoverse.github.com·
The State of the Octoverse | The State of the Octoverse explores a year of change with new deep dives into writing code faster, creating documentation and how we build sustainable communities on GitHub.
Stack Overflow Developer Survey 2021
Stack Overflow Developer Survey 2021
In May 2021 over 80,000 developers told us how they learn and level up, which tools they’re using, and what they want.
·insights.stackoverflow.com·
Stack Overflow Developer Survey 2021
Stack Overflow Developer Survey 2022
Stack Overflow Developer Survey 2022
In May 2022 over 70,000 developers told us how they learn and level up, which tools they’re using, and what they want.
·survey.stackoverflow.co·
Stack Overflow Developer Survey 2022
Go 语言垃圾收集器的实现原理
Go 语言垃圾收集器的实现原理
7.2 垃圾收集器 # 各位读者朋友,很高兴大家通过本博客学习 Go 语言,感谢一路相伴!《Go语言设计与实现》的纸质版图书已经上架京东,有需要的朋友请点击 链接 购买。 我们在上一节中详细介绍了 Go 语言内存分配器的设计与实现原理,分析了运行时内存管理组件之间的关系以及不同类型对象的分配,然而编程语言的内存管理系统除了负责堆内存的分配之外,它还需要负责回收不再使用的对象和内存空间,这部分职责是由本节即将介绍的垃圾收集器完成的。 在几乎所有的现代编程语言中,垃圾收集器都是一个复杂的系统,为了在不影响用户程序的情况下回收废弃的内存需要付出非常多的努力,Java 的垃圾收集机制是一个很好的例子,Java 8 中包含线性、并发、并行标记清除和 G1 四个垃圾收集器1,想要理解它们的工作原理和实现细节需要花费很多的精力。 本节会详细介绍 Go 语言运行时系统中垃圾收集器的设计与实现原理,我们不仅会讨论常见的垃圾收集机制、从 Go 语言的 v1.0 版本开始分析其演进过程,还会深入源代码分析垃圾收集器的工作原理。 7.2.1 设计原理 # 今天的编程语言通常会使用手动和自动两种方式管理内存,C、C++ 以及 Rust 等编程语言使用手动的方式管理内存2,工程师需要主动申请或者释放内存;而 Python、Ruby、Java 和 Go 等语言使用自动的内存管理系统,一般都是垃圾收集机制,不过 Objective-C 却选择了自动引用计数3,虽然引用计数也是自动的内存管理机制,但是我们在这里不会详细介绍它,本节的重点还是垃圾收集。 相信很多人对垃圾收集器的印象都是暂停程序(Stop the world,STW),随着用户程序申请越来越多的内存,系统中的垃圾也逐渐增多;当程序的内存占用达到一定阈值时,整个应用程序就会全部暂停,垃圾收集器会扫描已经分配的所有对象并回收不再使用的内存空间,当这个过程结束后,用户程序才可以继续执行,Go 语言在早期也使用这种策略实现垃圾收集,但是今天的实现已经复杂了很多。 图 7-21 内存管理的组件 在上图中,用户程序(Mutator)会通过内存分配器(Allocator)在堆上申请内存,而垃圾收集器(Collector)负责回收堆上的内存空间,内存分配器和垃圾收集器共同管理着程序中的堆内存空间。我们在这一节中将详细介绍 Go 语言垃圾收集中涉及的关键理论,帮助我们更好地理解本节剩下的内容。 标记清除 # 标记清除(Mark-Sweep)算法是最常见的垃圾收集算法,标记清除收集器是跟踪式垃圾收集器,其执行过程可以分成标记(Mark)和清除(Sweep)两个阶段: 标记阶段 — 从根对象出发查找并标记堆中所有存活的对象; 清除阶段 — 遍历堆中的全部对象,回收未被标记的垃圾对象并将回收的内存加入空闲链表; 如下图所示,内存空间中包含多个对象,我们从根对象出发依次遍历对象的子对象并将从根节点可达的对象都标记成存活状态,即 A、C 和 D 三个对象,剩余的 B、E 和 F 三个对象因为从根节点不可达,所以会被当做垃圾:
·draveness.me·
Go 语言垃圾收集器的实现原理
Go 语言内存分配器的实现原理
Go 语言内存分配器的实现原理
7.1 内存分配器 # 各位读者朋友,很高兴大家通过本博客学习 Go 语言,感谢一路相伴!《Go语言设计与实现》的纸质版图书已经上架京东,有需要的朋友请点击 链接 购买。 程序中的数据和变量都会被分配到程序所在的虚拟内存中,内存空间包含两个重要区域:栈区(Stack)和堆区(Heap)。函数调用的参数、返回值以及局部变量大都会被分配到栈上,这部分内存会由编译器进行管理;不同编程语言使用不同的方法管理堆区的内存,C++ 等编程语言会由工程师主动申请和释放内存,Go 以及 Java 等编程语言会由工程师和编译器共同管理,堆中的对象由内存分配器分配并由垃圾收集器回收。 不同的编程语言会选择不同的方式管理内存,本节会介绍 Go 语言内存分配器,详细分析内存分配的过程以及其背后的设计与实现原理。 7.1.1 设计原理 # 内存管理一般包含三个不同的组件,分别是用户程序(Mutator)、分配器(Allocator)和收集器(Collector)1,当用户程序申请内存时,它会通过内存分配器申请新内存,而分配器会负责从堆中初始化相应的内存区域。 图 7-1 内存管理的组件 Go 语言的内存分配器实现非常复杂,在分析内存分配器的实现之前,我们需要了解内存分配的设计原理,掌握内存的分配过程。这里会详细介绍内存分配器的分配方法以及 Go 语言内存分配器的分级分配、虚拟内存布局和地址空间。 分配方法 # 编程语言的内存分配器一般包含两种分配方法,一种是线性分配器(Sequential Allocator,Bump Allocator),另一种是空闲链表分配器(Free-List Allocator),这两种分配方法有着不同的实现机制和特性,本节会依次介绍它们的分配过程。 线性分配器 # 线性分配(Bump Allocator)是一种高效的内存分配方法,但是有较大的局限性。当我们使用线性分配器时,只需要在内存中维护一个指向内存特定位置的指针,如果用户程序向分配器申请内存,分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置,即移动下图中的指针: 图 7-2 线性分配器 虽然线性分配器实现为它带来了较快的执行速度以及较低的实现复杂度,但是线性分配器无法在内存被释放时重用内存。如下图所示,如果已经分配的内存被回收,线性分配器无法重新利用红色的内存: 图 7-3 线性分配器回收内存 因为线性分配器具有上述特性,所以需要与合适的垃圾回收算法配合使用,例如:标记压缩(Mark-Compact)、复制回收(Copying GC)和分代回收(Generational GC)等算法,它们可以通过拷贝的方式整理存活对象的碎片,将空闲内存定期合并,这样就能利用线性分配器的效率提升内存分配器的性能了。 因为线性分配器需要与具有拷贝特性的垃圾回收算法配合,所以 C 和 C++ 等需要直接对外暴露指针的语言就无法使用该策略,我们会在下一节详细介绍常见垃圾回收算法的设计原理。 空闲链表分配器 # 空闲链表分配器(Free-List Allocator)可以重用已经被释放的内存,它在内部会维护一个类似链表的数据结构。当用户程序申请内存时,空闲链表分配器会依次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并修改链表: 图 7-4 空闲链表分配器 因为不同的内存块通过指针构成了链表,所以使用这种方式的分配器可以重新利用回收的资源,但是因为分配内存时需要遍历链表,所以它的时间复杂度是 $O(n)$。空闲链表分配器可以选择不同的策略在链表中的内存块中进行选择,最常见的是以下四种: 首次适应(First-Fit)— 从链表头开始遍历,选择第一个大小大于申请内存的内存块; 循环首次适应(Next-Fit)— 从上次遍历的结束位置开始遍历,选择第一个大小大于申请内存的内存块; 最优适应(Best-Fit)— 从链表头遍历整个链表,选择最合适的内存块; 隔离适应(Segregated-Fit)— 将内存分割成多个链表,每个链表中的内存块大小相同,申请内存时先找到满足条件的链表,再从链表中选择合适的内存块; 上述四种策略的前三种就不过多介绍了,Go 语言使用的内存分配策略与第四种策略有些相似,我们通过下图了解该策略的原理:
·draveness.me·
Go 语言内存分配器的实现原理
OI Wiki
OI Wiki
OI Wiki 是一个编程竞赛知识整合站点,提供有趣又实用的编程竞赛知识以及其他有帮助的内容,帮助广大编程竞赛爱好者更快更深入地学习编程竞赛
·oi-wiki.org·
OI Wiki
链表 - OI Wiki
链表 - OI Wiki
OI Wiki 是一个编程竞赛知识整合站点,提供有趣又实用的编程竞赛知识以及其他有帮助的内容,帮助广大编程竞赛爱好者更快更深入地学习编程竞赛
·oi-wiki.org·
链表 - OI Wiki
Go内存分配那些事,就这么简单!
Go内存分配那些事,就这么简单!
新老朋友好久不见,我是大彬,这篇文章准备了很久,不是在拖延,而是中间做了一些其他事情,耽搁了一些。 这篇文章主要介绍Go内存分配和Go内存管理,会轻微涉及内存申请和释放,以及Go垃圾回收。 从非常宏观的角度看,Go的内存管理就是下图这个样子,我们今天主要关注其中标红的部分。 友情提醒: 文章有点长,建议先收藏,后阅读,绝对是学习内存管理的好资料。 本文基于go1.11.2,不同版本Go的内存
·lessisbetter.site·
Go内存分配那些事,就这么简单!
golang-GetPagesize()内存页的介绍-内存分页大小对性能的提升原理 - Go语言中文网 - Golang中文社区
golang-GetPagesize()内存页的介绍-内存分页大小对性能的提升原理 - Go语言中文网 - Golang中文社区
JVM优化之调整大内存分页(LargePage) 本文将从内存分页的原理,如何调整分页大小两节内容,向你阐述LargePage对JVM的性能有何提升作用,并在文末点明了大内分页的副作用。OK,让我们开始吧! 内存分页大小对性能的提升原理 首先,我们需要回顾一小部分计算机组成原理,这对理解大内存分页至于JVM性能的提升是有好处的。 什么是内存分页? 我们知道,CPU是通过寻址来访问内存的。32位CP
·studygolang.com·
golang-GetPagesize()内存页的介绍-内存分页大小对性能的提升原理 - Go语言中文网 - Golang中文社区
大内存时代——为什么PageSize仍不建议选择16KB或64KB?其实我们有更好的选择
大内存时代——为什么PageSize仍不建议选择16KB或64KB?其实我们有更好的选择
“640K is enough for everybody”——比尔 盖茨 可惜有趣的历史几乎总是假的。 1996年,盖茨在接受媒体采访时澄清了有关“640K内存”的传闻:“我虽说过一些蠢话,做过一些傻事,可这句话绝对不是我说的。业界从…
·zhuanlan.zhihu.com·
大内存时代——为什么PageSize仍不建议选择16KB或64KB?其实我们有更好的选择
解答:x86架构下,页面大小为什么是4K?_乾龙_Heron的博客-CSDN博客
解答:x86架构下,页面大小为什么是4K?_乾龙_Heron的博客-CSDN博客
前提:32位逻辑地址空间的计算机系统,三级页表,每个页中每个条目占4Byte,即32位的数据以上前提是目前x86架构32位系统的真实情况设:页大小为X(byte)则:X/4就是每个页中可以存取的条目个数两级页表的地址转化关系如图1所示:图1:假设在一个32位的条目中存放此内存的地址,则2*log2(X/4)就是图1中,p1+p2的位数。2^(p1+p2):系统可以寻...
·blog.csdn.net·
解答:x86架构下,页面大小为什么是4K?_乾龙_Heron的博客-CSDN博客
Go 语言设计与实现
Go 语言设计与实现
Go 语言设计与实现 # 各位读者朋友,很高兴大家通过本博客学习 Go 语言,感谢一路相伴!《Go语言设计与实现》的纸质版图书已经上架京东,有需要的朋友请点击 链接 购买。 历史和现状 # Go 语言是诞生于 2009 年的编程语言,发展到今天已经有过去了 10 多年1。目前的 Go 语言在国内外的社区都非常热门,很多著名的开源框架,例如:Kubernetes、etcd 和 Prometheus 等都使用 Go 语言开发,近年来热门的微服务架构和云原生技术也为 Go 语言社区带来了非常多的活力。 图 1 - Go 语言 作者目前也使用 Go 语言作为日常开发的主要语言,虽然 Go 语言没有 Lisp 系语言的开发效率和强大表达能力,但是却是一门非常容易使用并且大规模运用的工程语言,这也是作者学习和使用 Go 语言的主要原因。 作者是从 2018 年才开始学习和使用 Go 语言的,刚刚接触 Go 语言时是有些排斥和拒绝的,一度认为 Go 语言 GOPATH 的设计非常诡异,而简单的语法也导致了低下的表达能力并且影响开发效率。但是随着对 Go 语言的深入学习和理解,作者的这一观念也在不断改变。 到了今天,作者认为我们在工业界需要这么一门语法简单的编译型语言,它能够提供简单的抽象和概念,虽然目前 Go 语言也有很多问题,但是语言以及周边工具的不断完善也让作者感受到了社区的活力,也坚定地认为这门语言未来的发展会越来愈好。 为什么要写这本书 # 目前的市面上分析 Go 语言实现的书籍较少,多数的书籍都偏重于 Go 语言基础和实战。虽然目前有很多分析 Go 语言的博客,但是它们却都面临以下的两个问题:
·draveness.me·
Go 语言设计与实现
Go 语言的栈内存和逃逸分析
Go 语言的栈内存和逃逸分析
7.3 栈空间管理 # 各位读者朋友,很高兴大家通过本博客学习 Go 语言,感谢一路相伴!《Go语言设计与实现》的纸质版图书已经上架京东,有需要的朋友请点击 链接 购买。 应用程序的内存一般会分成堆区和栈区,程序在运行期间可以主动从堆区申请内存空间,这些内存由内存分配器分配并由垃圾收集器负责回收,我们在上两节已经详细分析了堆内存的申请和释放过程,本节会介绍 Go 语言栈内存的管理。 7.3.1 设计原理 # 栈区的内存一般由编译器自动分配和释放,其中存储着函数的入参以及局部变量,这些参数会随着函数的创建而创建,函数的返回而消亡,一般不会在程序中长期存在,这种线性的内存分配策略有着极高地效率,但是工程师也往往不能控制栈内存的分配,这部分工作基本都是由编译器完成的。 寄存器 # 寄存器1是中央处理器(CPU)中的稀缺资源,它的存储能力非常有限,但是能提供最快的读写速度,充分利用寄存器的速度可以构建高性能的应用程序。寄存器在物理机上非常有限,然而栈区的操作会使用到两个以上的寄存器,这足以说明栈内存在应用程序的重要性。 栈寄存器是 CPU 寄存器中的一种,它的主要作用是跟踪函数的调用栈2,Go 语言的汇编代码包含 BP 和 SP 两个栈寄存器,它们分别存储了栈的基址指针和栈顶的地址,栈内存与函数调用的关系非常紧密,我们在函数调用一节中曾经介绍过栈区,BP 和 SP 之间的内存就是当前函数的调用栈。 图 7-43 栈寄存器与内存 因为历史原因,栈区内存都是从高地址向低地址扩展的,当应用程序申请或者释放栈内存时只需要修改 SP 寄存器的值,这种线性的内存分配方式与堆内存相比更加快速,仅会带来极少的额外开销。 线程栈 # 如果我们在 Linux 操作系统中执行 pthread_create 系统调用,进程会启动一个新的线程,如果用户没有通过软资源限制 RLIMIT_STACK 指定线程栈的大小,那么操作系统会根据架构选择不同的默认栈大小3。 架构 默认栈大小 i386 2 MB IA-64 32 MB PowerPC 4 MB … … x86_64 2 MB 表 7-4 架构和线程默认栈大小
·draveness.me·
Go 语言的栈内存和逃逸分析