内存管理:Golang、Python、Linux
0、如果想要实现一门语言的内存管理,该怎么设计?
1.内存池:向系统申请大块内存,然后进行管理和分配(管理内存分配)。
2.垃圾回收:当分配的内存使用完之后,不直接归还给系统,而是归还给内存池,方便进行下一次复用。至于垃圾回收选择标记回收,还是分代回收算法应该符合语言设计初衷。
3.大小切分:使用单独的数组或者链表,把需要申请的内存大小向上取整,直接从这个数组或链表拿出对应的大小内存块,方便分配内存。大的对象以页申请内存,小的对象以块来申请,避免内存碎片,提高内存使用率。
4.多线程管理:每个线程应该有自己的内存块,这样避免同时访问共享区的时候加锁,提升语言的并发性,线程之间通信使用消息队列的形式,一定不要使用共享内存的方式。提供全局性的分配链,如果线程内存不够用了,可向分配链申请内存。
1、Golang
Go语言自带垃圾回收功能,大多数情况下是不需要用户自己管理内存的;tcmalloc(thread-caching mallo)是google推出的一种内存分配器。
具体策略:全局缓存堆和进程的私有缓存。golang语言中MHeap就是全局缓存堆,MCache作为线程私有缓存。
golang内存分配器主要包含三个数据结构:MHeap,MCentral、MCache。
1、MHeap:分配堆,主要是负责向系统申请大块的内存,为下层MCentral和MCache提供内存服务。它管理的基本单位是MSpan(若干连续内存页的数据结构)。MSpan是一个双端链表的形式,里面存储了它的一些位置信息。通过一个基地址+(页号*页大小),就可以定位到这个MSpan的实际内存空间。
2、MCache:运行时分配池,不针对全局,而是每个线程都有自己的局部内存缓存MCache,他是实现goroutine高并发的重要因素,因为分配小对象可直接从MCache中分配,不用加锁,提升了并发效率。
3、MCentral:作为MHeap和MCache的承上启下的连接。承上,从MHeap申请MSpan;启下,将MSpan划分为各种尺寸的对象提供给MCache使用。
给对象 object 分配内存的主要流程:
- object size > 32K,则使用 mheap 直接分配。
- object size < 16 byte,使用 mcache 的小对象分配器 tiny 直接分配。 (其实 tiny 就是一个指针,暂且这么说吧。)
- object size > 16 byte && size <=32K byte 时,先使用 mcache 中对应的 size class 分配。
- 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。
- 如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。
- 如果 mheap 也没有合适的 span,则想操作系统申请。
总结:
- MHeap是一个全局变量,负责向系统申请内存,mallocinit()函数进行初始化。如果分配内存对象大于32K直接向MHeap申请。
- MCache线程级别管理内存池,关联结构体P,主要是负责线程内部内存申请。
- MCentral连接MHeap与MCache的,MCache内存不够则向MCentral申请,MCentral不够时向MHeap申请内存。
2、Python
?python中万物皆对象,python的存储问题是对象的存储问题,并且对于每个对象,python会分配一块内存空间去存储它
Python的内存管理机制:引入计数、垃圾回收、内存池机制
Python垃圾回收主要以引用计数为主,标记?清除和分代清除为辅的机制,其中标记?清除和分代回收主要是为了处理循环引用的难题
一、变量与对象
- 变量,通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值.
- 对象,类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)注意:变量名没有类型,类型属于对象(因为变量引用对象,所以类型随对象),变量引用什么类型的对象,变量就是什么类型的.
- 引用所指判断,通过is进行引用所指判断,is是用来判断两个引用所指的对象是否相同.
二、引用计数
- 在Python中,每个对象都有指向该对象的引用总数---引用计数,查看对象的引用计数:sys.getrefcount()
三、垃圾回收
- ? 当Python中的对象越来越多,占据越来越大的内存,启动垃圾回收(garbage collection),将没用的对象清除.
四、内存池机制
Python中有分为大内存和小内存:(256K为界限分大小内存)
- 大内存使用malloc进行分配
- 小内存使用内存池进行分配
- Python的内存池(金字塔)
Python的内存管理是由私有heap空间管理的.所有的Python对象和数据结构都在一个私有heap中.程序员没有访问该heap的权限,只有解释器才能对它进行操作.为Python的heap空间分配内存是由Python的内存管理模块进行的,其核心API会提供一些访问该模块的方法供程序员使用.Python有自带的垃圾回收系统,它回收并释放没有被使用的内存,让它们能够被其他程序使用;
3、Linux
内存管理系统可以分为两部分,分别是内核空间内存管理和用户空间内存管理:
————内存管理子系统的职责是:进程请求内存时分配可用内存,进程释放内存后回收内存,以及跟踪系统内存使用情况。现代操作系统要求能够使多个程序共享系统资源,同时要求内存限制对于开发者是透明的。在这种情况下,虚拟内存应运而生。虚拟内存可以使得进程可以访问比实际内存大得多的空间,并且使得多个程序共享内存显得更加有效。
————当程序从内存中取得数据的时候,需要使用地址指出需要访问的内存位置(注意:这个地址是虚拟地址,他们组成的进程的虚拟地址空间)。每个进程都有自己的虚拟地址空间,这样做的好处是可以防止非法读取或覆盖其他进程的数据(虚拟地址允许进程使用超过物理内存的内存空间,因此操作系统可以给每个进程提供独立的虚拟线性地址空间。)
<页>
a:作为内存管理的基本单元,页的许多状态需要被记录下来(比如,内核需要知道什么时候可以被回收),因此内核为内核中的每个页都准备了页描述符struct page{}.系统在初始化时根据物理内存的大小建立起一个page结构数组mem_map,作为物理页面的“仓库”
由此可见,slab时间上由许多缓存组成。缓存分为"专用"和"通用"。专用缓存保存特定对象的内存区,比如各种描述符,比如进程描述符"struct task_structs".
<用户空间/进程内存管理>
-------以上讨论了内核如何管理自己的内存空间,接下来讨论用户空间如何让管理自己的内存空间。用户进程创建后需要分配一个虚拟地址空间,并且可用通过增加或删除地址间隔得以扩大或缩小。(地址间隔(一段地址空间):是一种内存单元,也被称作内存范围或内存区,把进程地址空间划分为不同的区域是有用的,不同的区域具有不同的保护方案和访问属性,比如".text"".data"".bss""栈""栈")。