内存管理:Golang、Python、Linux

cleanerxiaoqiang 2020-05-08

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 分配内存的主要流程:

  1. object size > 32K,则使用 mheap 直接分配。
  2. object size < 16 byte,使用 mcache 的小对象分配器 tiny 直接分配。 (其实 tiny 就是一个指针,暂且这么说吧。)
  3. object size > 16 byte && size <=32K byte 时,先使用 mcache 中对应的 size class 分配。
  4. 如果 mcache 对应的 size class 的 span 已经没有可用的块,则向 mcentral 请求。
  5. 如果 mcentral 也没有可用的块,则向 mheap 申请,并切分。
  6. 如果 mheap 也没有合适的 span,则想操作系统申请。

总结:

  1. MHeap是一个全局变量,负责向系统申请内存,mallocinit()函数进行初始化。如果分配内存对象大于32K直接向MHeap申请。
  2. MCache线程级别管理内存池,关联结构体P,主要是负责线程内部内存申请。
  3. MCentral连接MHeap与MCache的,MCache内存不够则向MCentral申请,MCentral不够时向MHeap申请内存。

2、Python

?python中万物皆对象,python的存储问题是对象的存储问题,并且对于每个对象,python会分配一块内存空间去存储它

Python的内存管理机制:引入计数、垃圾回收、内存池机制

Python垃圾回收主要以引用计数为主,标记?清除分代清除为辅的机制,其中标记?清除和分代回收主要是为了处理循环引用的难题

一、变量与对象

  • 变量,通过变量指针引用对象,变量指针指向具体对象的内存空间,取对象的值.
  • 对象,类型已知,每个对象都包含一个头部信息(头部信息:类型标识符和引用计数器)注意:变量名没有类型,类型属于对象(因为变量引用对象,所以类型随对象),变量引用什么类型的对象,变量就是什么类型的.
  • 引用所指判断,通过is进行引用所指判断,is是用来判断两个引用所指的对象是否相同.

二、引用计数

  • 在Python中,每个对象都有指向该对象的引用总数---引用计数,查看对象的引用计数:sys.getrefcount()

三、垃圾回收

  • ? 当Python中的对象越来越多,占据越来越大的内存,启动垃圾回收(garbage collection),将没用的对象清除.

四、内存池机制

Python中有分为大内存和小内存:(256K为界限分大小内存)

  1. 大内存使用malloc进行分配
  2. 小内存使用内存池进行分配
  3. Python的内存池(金字塔)

Python的内存管理是由私有heap空间管理的.所有的Python对象和数据结构都在一个私有heap中.程序员没有访问该heap的权限,只有解释器才能对它进行操作.为Python的heap空间分配内存是由Python的内存管理模块进行的,其核心API会提供一些访问该模块的方法供程序员使用.Python有自带的垃圾回收系统,它回收并释放没有被使用的内存,让它们能够被其他程序使用;

3、Linux

内存管理系统可以分为两部分,分别是内核空间内存管理和用户空间内存管理:

————内存管理子系统的职责是:进程请求内存时分配可用内存,进程释放内存后回收内存,以及跟踪系统内存使用情况。现代操作系统要求能够使多个程序共享系统资源,同时要求内存限制对于开发者是透明的。在这种情况下,虚拟内存应运而生。虚拟内存可以使得进程可以访问比实际内存大得多的空间,并且使得多个程序共享内存显得更加有效。

————当程序从内存中取得数据的时候,需要使用地址指出需要访问的内存位置(注意:这个地址是虚拟地址,他们组成的进程的虚拟地址空间)。每个进程都有自己的虚拟地址空间,这样做的好处是可以防止非法读取或覆盖其他进程的数据(虚拟地址允许进程使用超过物理内存的内存空间,因此操作系统可以给每个进程提供独立的虚拟线性地址空间。)

<页>

a:作为内存管理的基本单元,页的许多状态需要被记录下来(比如,内核需要知道什么时候可以被回收),因此内核为内核中的每个页都准备了页描述符struct page{}.系统在初始化时根据物理内存的大小建立起一个page结构数组mem_map,作为物理页面的“仓库”

 k:slab分配器
--------我们知道页是Linux内存管理的基本单元。但是进程往往会以字节的为单位请求内存,如果照样给其分配一个页面,这样显得浪费内存,为了解决这个问题并实这种小块内存请求。内核开发者们实现了slab分配器。
slab分配器为了减少内存分配,初始化,销毁和释放的代价,通常会把经常使用的内存区以缓存的方式对待,并加以维护(即比如系统经常会使用task_struct ,这就将该结构体以缓存方式常驻内存),当进程不在需要该内存区时,就会把该内存放入缓冲区。

由此可见,slab时间上由许多缓存组成。缓存分为"专用"和"通用"。专用缓存保存特定对象的内存区,比如各种描述符,比如进程描述符"struct task_structs".

<用户空间/进程内存管理>

-------以上讨论了内核如何管理自己的内存空间,接下来讨论用户空间如何让管理自己的内存空间。用户进程创建后需要分配一个虚拟地址空间,并且可用通过增加或删除地址间隔得以扩大或缩小。(地址间隔(一段地址空间):是一种内存单元,也被称作内存范围或内存区,把进程地址空间划分为不同的区域是有用的,不同的区域具有不同的保护方案和访问属性,比如".text"".data"".bss""栈""栈")。

相关推荐