为什么要有虚拟内存?
答案:为了在多线程环境下,各个进程之间的内存地址不受影响。每个进程都会有一套独立的虚拟地址空间。虚拟内存和物理内存之间的映射操作系统会完成,程序只需要考虑虚拟内存就可以了。
当物理内存不够用的时候,操作系统会通过内存交换技术将不常使用的内存换出到硬盘,等到需要的时候再加载回来。
内存分配的四种方式?
内存分段的存储管理方式:
这种方式下会将一个程序分成若干个逻辑分段,比如代码段,数据段,栈段,堆段。
在分段方式下的虚拟地址和物理地址的映射:分段下的虚拟地址会记录段号和一个段内偏移量,通过段号在段表里面查找到该段的基地址,然后基地址+偏移量就是实际要访问的物理地址了。
分段分配的弊端:
内存碎片:内存碎片分为内部内存碎片和外部内存碎片。内存分段管理可以根据各个段的需要求分配对应大小的段,不会有内部内存碎片。外部内存碎片是由于多个段不能恰好使用所有的内存空间,产生了多个不连续的小块物理内存。内存交换就是用于解决这个问题的。
内存交换空间:位于硬盘空间,用于内存和硬盘空间的交换。
内存交换效率低:内存交换是将不常用的内存暂时移动到磁盘交换空间,给其他任务腾空间。
如果交换的是一个内存占用很大的程序,会出现较大的卡顿。
内存分页的存储管理方式:
分页的情况下会将虚拟内存和物理内存都划分成一段段固定大小的页,在linux中,一个页的大小是4kb。
在分页方式下的虚拟地址号和物理地址的映射:在内存中存储了一张页表,会有一个内存管理单元(MMU)完成将虚拟地址转换成物理地址的工作。虚拟地址里面会有页号和页内偏移,页面里面记录了虚拟页号对应的物理页号。物理页号的地址+页内偏移就是要访问的地址了。
分页如何解决外部内存碎片和内存交换效率低的问题?
内存分页是紧密排列的,不会有外部内存碎片。但是分配的最小单位是页,即使程序大小不足一页也会进行分配,所以会有内部内存碎片的问题。
如果内存空间不够,系统会使用LRU算法将内存页面释放。一次性写入磁盘的只有一个页或者是几个页,所以内存交换的效率比较高。
段页式的存储管理方式:
段页式的情况下会先将程序划分为多个有意义的段,比如代码段,数据段,栈段。然后每个段都划分为多个页。物理内存则是采用分页的方式。
在段页方式下的虚拟地址号和物理地址的映射:虚拟地址的结构是段号+段内页号+页内位移。
用于地址转换的数据结构是一张段表+多张页表。段表里存储了每个段的页表的地址。页表里存了这个段的所有页的物理页号。
Linux 内存布局
堆段的作用是为程序里面的行为动态分配和释放内存。主要存了程序运行中创建的数据结构,比如数组,链表,树等等。
文件映射段用于将文件的内容之间映射到进程的地址空间,可以想访问内存一样访问文件内容,提高了文件IO的效率。零拷贝技术里面也有用到这个,mmap()减少了一次从内核空间到用户空间的拷贝,文件只从磁盘拷贝到了内核空间。
malloc 是如何分配内存的?
malloc() 分配的是物理内存吗?
不,malloc()分配的是虚拟内存。如果分配后的虚拟内存没有被访问的话,是不会映射到物理内存的。
只有在访问已分配的虚拟地址空间时,系统超找页表发现没有对应的页表项,才会触发缺页中断。 然后操作系统会去建立虚拟内存和物理内存的映射关系。
malloc(1)会分配多大的虚拟内存?
反正不是一个字节大小的内存。
free 释放内存,会归还给操作系统吗?
不会,堆内存会保留这个内存到malloc的内存池里,等待下次申请直接复用。只有进程退出的时候才会回收所有资源。(只针对malloc通过brk()方式申请内存)
mmap在文件映射段分配的内存会被归还。
为什么不全部使用mmap分配内存?
总结下来就是两点原因。
减少用户态和内核态的切换。
减少缺页中断的发生次数。
为什么不全部使用brk()分配内存?
brk会有大量的内存碎片,所以默认大块的内存用mmap进行分配。
为什么free() 函数只传入一个内存地址,为什么能知道要释放多大的内存?
malloc返回给用户态的内存起始地址比进程的堆空间起始地址多了16字节。这16字节就是用于保存这个内存块的描述信息的,包括内存块的大小。
内存满了,会发生什么?
内存满了会发生OOM,在内存满之前应该会先尝试进行回收内存的工作。
哪些内存是可以被回收的?
文件页是那些硬盘里有的数据页。
匿名页则是程序运行生成的动态数据页,堆栈数据,这些需要通过内存交换持久化到磁盘中。