前言

上一篇介绍完了虚拟内存,这篇将要给内存管理收个尾,介绍以下内存是如何回收的。

这里所要讲的内存回收,并不是虚拟内存中的页面置换(当可使用的物理空间不足时,需要把部分页换出),而是指对用户空间中的堆段和文件映射段进行回收(用户使用 malloc、mmap 等分配出去的空间),相当于操作系统层面的自动的 free()

内存泄露

Memory Leak,内存泄漏是指向系统申请分配内存,可是使用完了以后却不归还,结果系统也不能再次将它分配给需要的程序。

内存回收

内存回收指的是对用户空间中的堆段和文件映射段进行回收(用户使用 malloc、mmap 等分配出去的空间)。用户可以手动地使用 free() 等进行内存释放。当没有空闲的物理内存时,内核就会开始自动地进行回收内存工作。回收的方式主要是两种:后台内存回收和直接内存回收。

  • 后台内存回收(kswapd):在物理内存紧张的时候,会唤醒 kswapd 内核线程来回收内存,这个回收内存的过程异步的,不会阻塞进程的执行。
  • 直接内存回收(direct reclaim):如果后台异步回收跟不上进程内存申请的速度,就会开始直接回收,这个回收内存的过程是同步的,会阻塞进程的执行。

如果直接内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会触发 OOM (Out of Memory)机制,根据算法选择一个占用物理内存较高的进程,然后将其杀死,释放内存资源,直到释放足够的内存。

虽然在编程语言层面就已经提供了垃圾回收机制(Garbage Collection),但是当程序申请内存的速度远远大于内存回收的速度,还是会发生内存不足,所以还需要操作系统实现内存回收。

可被回收的内存类型

主要有两类内存可以被回收,而且它们的回收方式也不同。

  • 文件页(File-backed Page):内核缓存的磁盘数据(Buffer)和内核缓存的文件数据(Cache)都叫作文件页。大部分文件页,都可以直接释放内存,以后有需要时,再从磁盘重新读取就可以了。而那些被应用程序修改过,并且暂时还没写入磁盘的数据(也就是脏页),就得先写入磁盘,然后才能进行内存释放。所以,回收干净页的方式是直接释放内存,回收脏页的方式是先写回磁盘后再释放内存
  • 匿名页(Anonymous Page):这部分内存没有实际载体,不像文件缓存有硬盘文件这样一个载体,比如堆、栈数据等。这部分内存很可能还要再次被访问,所以不能直接释放内存,它们回收的方式是通过操作系统的 Swap 机制,把不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用。再次访问这些内存时,重新从磁盘读入内存就可以了。

文件页和匿名页的回收都是基于 LRU (最近最少使用)算法的。回收内存的操作基本都会发生磁盘 I/O,如果回收内存的操作很频繁,意味着磁盘 I/O 次数会很多,会影响系统的性能。

总结

虽然编程语言中有垃圾回收机制(Garbage Collection),可以回收用户申请的内存空间,但是由于存在申请速度远大于回收速度、内存使用后未释放等情况,所以操作系统还需要实现内存回收机制来兜底。

回收的方式有后台内存回收、直接内存回收和 OOM。后台内存回收是异步的,不会阻塞进程,回收速度较慢;直接内存回收是同步的,会阻塞进程,回收速度快;如果回收的速度仍然赶不上,则会触发 OOM 机制,以占用内存从高到低依次杀死进程,直到内存足够使用为止。

番外

malloc是如何分配内存的?

知道了用户空间的堆段和文件映射段是如何被回收的,那他们是怎么被分配的呢?

在虚拟内存的用户空间分段中曾经讲过,堆段是用来存储动态分配的内存的,是由用户自行申请使用的。接下来以最常见的 C 语言中的 malloc() 为例进行说明。

malloc() 并不是系统调用,而是 C 库里的函数,用于动态分配内存。malloc 申请内存的时候,会有两种方式向操作系统申请堆内存。

  • 如果用户分配的内存小于 128 KB,通过 brk() 系统调用从堆分配内存;
  • 如果用户分配的内存大于 128 KB,通过 mmap() 系统调用在文件映射区域分配内存;

方式一实现的方式很简单,就是通过 brk() 函数将「堆顶」指针向高地址移动,获得新的内存空间。

方式二通过 mmap() 系统调用中「私有匿名映射」的方式,在文件映射区分配一块内存,也就是从文件映射区“偷”了一块内存。

malloc() 分配的是虚拟内存,如果分配后的虚拟内存没有被访问的话,是不会将虚拟内存不会映射到物理内存,这样就不会占用物理内存了。只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发缺页中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。

free 释放:

malloc 返回给用户态的内存起始地址会比进程的堆空间起始地址多 16 字节,用于保存了该内存块的描述信息,比如有该内存块的大小。

这样当执行 free() 函数时,free 会对传入进来的内存地址向左偏移 16 字节,然后从这个 16 字节的分析出当前的内存块的大小,自然就知道要释放多大的内存了。

  • malloc 通过 brk() 方式申请的内存,free 释放内存的时候,并不会把内存归还给操作系统,而是缓存在 malloc 的内存池中,待下次使用
  • malloc 通过 mmap() 方式申请的内存,free 释放内存的时候,会把内存归还给操作系统,内存得到真正的释放

优点:

  • 通过 brk() 方式申请内存,会预分配更大的内存来作为内存池,当内存释放的时候,就缓存在内存池中。等下次再申请内存的时候,就直接从内存池取出对应的内存块就行了,而且可能这个内存块的虚拟地址与物理地址的映射关系还存在,这样不仅减少了系统调用的次数,也减少了缺页中断的次数,这将大大降低 CPU 的消耗。
  • 通过mmap() 方式申请内存,释放时不会留下碎片空间。

缺点:

  • 通过 brk() 方式申请内存,释放时会留下碎片空间,可能导致内存泄露。
  • 通过mmap() 方式申请内存,不仅每次都会发生运行态的切换,还会发生缺页中断(在第一次访问虚拟地址后),这样会导致 CPU 消耗较大。

内存管理(五)——内存回收相关推荐

  1. 详解JVM内存管理与垃圾回收机制2 - 何为垃圾

    随着编程语言的发展,GC的功能不断增强,性能也不断提高,作为语言背后的无名英雄,GC离我们的工作似乎越来越远.作为Java程序员,对这一点也许会有更深的体会,我们不需要了解太多与GC相关的知识,就能很 ...

  2. .NET基础 (05)内存管理和垃圾回收

    内存管理和垃圾回收 1 简述.NET中堆栈和堆的特点和差异 2 执行string abc="aaa"+"bbb"+"ccc"共分配了多少内存 ...

  3. 【转】.NET内存管理、垃圾回收

    ? .NET内存管理.垃圾回收 1. Stack和Heap     每个线程对应一个stack,线程创建的时候CLR为其创建这个stack,stack主要作用是记录函数的执行情况.值类型变量(函数的参 ...

  4. python内存管理和释放_《python解释器源码剖析》第17章--python的内存管理与垃圾回收...

    17.0 序 内存管理,对于python这样的动态语言是至关重要的一部分,它在很大程度上决定了python的执行效率,因为在python的运行中会创建和销毁大量的对象,这些都设计内存的管理.同理pyt ...

  5. Python内存管理以及垃圾回收机制

    垃圾回收:用通俗点的语言解释就是内存管理和垃圾回收的过程. 大管家refchain 在Python的C源码中有一个名为refchain的环状双向链表,这个链表就比较厉害了,因为Python程序中一旦创 ...

  6. 详解JVM内存管理与垃圾回收机制5 - Java中的4种引用类型

    在Java语言中,除了基础数据类型的变量以外,其他的都是引用类型,指向各种不同的对象.在前文我们也已经知道,Java中的引用可以是认为对指针的封装,这个指针中存储的值代表的是另外一块内存的起始地址(对 ...

  7. JavaScript性能优化【上】-- 内存管理、垃圾回收

    JavaScript 内存管理 内存为什么需要管理 function fn () {arrList = []arrList[100000] = 'lg is a coder'}fn() // 内存泄露 ...

  8. python内存的回收机制_python的内存管理和垃圾回收机制详解

    简单来说python的内存管理机制有三种 1)引用计数 2)垃圾回收 3)内存池 接下来我们来详细讲解这三种管理机制 1,引用计数: 引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用 ...

  9. Go 内存管理与垃圾回收

    本文章主要从原理层面分析 Go 的内存管理和垃圾回收机制,包括堆内存.栈内存和垃圾回收等,对于源码的分析涉及较少,对源码有兴趣的朋友可以查看文末的参考链接进行查看,都是写的很好的文章,本文大部分都是从 ...

最新文章

  1. ASP.Net 中Frames 的一些使用说明...
  2. Python数据可视化1.2 数据转换
  3. 2018/7/17-纪中某C组题【jzoj4024,jzoj4025,jzoj2136,jzoj2137】
  4. ps发光插件_PS插件自定义区域发光真实辉光插件 Oniric Glow Generator for Photoshop【资源分享1444】...
  5. 考研复习安排——第一阶段末
  6. TP3.2项目 MySQL5.7报错1055 group by新特性
  7. VS2010/MFC编程入门之二(VS2010应用程序工程中文件的组成结构)
  8. 电商大促特辑:蘑菇街致美丽新世界的架构礼
  9. imo free video calls and text(IMO免费视频通话)
  10. 利用python代码 之将指定网易云歌单保存到Excel中
  11. 实现轮播模拟点击事件
  12. void 和 void *区别(c++)
  13. python游戏引擎3d_一个人独立开发 3D 游戏引擎可能吗?
  14. centos 下 yum安装python3
  15. leetcode--给房子涂色III
  16. docker保存镜像、打包tar、加载tar镜像
  17. 广告营销核心干货——《我的营销心得》读书笔记2900字
  18. 六行python代码的爱心曲线_6行python代码的爱心曲线
  19. SpriteAtlas
  20. 麒麟os或将取代android,新款操作系统将要诞生!华为研发麒麟OS,网友:取代安卓...

热门文章

  1. 利用不蒜子统计网站的访问量
  2. (17)雅思屠鸭第十七天:小作文完整攻略
  3. 【三角形判断】测试用例设计
  4. 关于Mongodb的全面总结
  5. Python实现BT种子转化为磁力链接【实战】
  6. linux挂载u盘在哪个文件夹,如何在Linux挂载U盘
  7. 计算机一级b和小高考,小高考B是几分相关内容
  8. SSO单点登录-分布式系统实战
  9. 《Federated_Machine_Learning:Concept_and_Applications》精读
  10. 操作系统基础知识用户态和内核态的区别