第四周读书笔记
《c++应用程序性能优化》读书笔记(part2)
本周看的是这本书的第二篇章,收获不小。第二篇章是从内存的角度来写程序优化的,分别从操作系统的内存管理、动态内存管理和内存池三个方面去介绍内存使用的优化。
写程序不可能不考虑内存的使用情况,因此,对内存的优化是十分有必要的。第四章介绍虚拟内存管理。进程的虚拟地址空间中的页有三种状态:自由(free),预留(reserved),提交(committed)。Free表示此页内存没有分配,可供新内存分配;reserved表示该块内存不可被自由分配,而是只能给后面的预留的代码段使用;committed和reserved的作用相似,都是给预留的代码段分配非自由内存。但是reserved只是用记号表明这块内存已经预留,直到代码段需要内存的时候才会去申请真实的物理内存,committed则是直接申请了一块真实的物理内存,所以committed要比reserved慢一点。但是值得注意的是,因为没有物理存储,所以当访问“预留”的空间时会发生“内存访问违例”(这会导致整个进程退出),而提交操作仅仅是开辟调页文件区域的空间而已,物理内存没有分配,虚拟内存跟物理内存映射所必须的页目录和页表也没有建。必须到第一次访问时才全部完善。Reserved和committed是win32管理内存的一种策略。虚拟内存管理器维护一个称为“页帧数据库(page-frame database)”的数据结构,此数据结构是操作系统全局的,当windows启动时被初始化,用来跟踪和记录物理内存中每一个页的状态,它会用一个链表将所有空闲页连接起来,当需要空闲页时,直接查找此空闲页链表,如果有,直接使用;否则根据调页算法置换之。另外,操作系统充分利用了局部性原理,每一次发生缺页中断时,调入内存的页面是需求页面相邻的几个页面。当要有了空闲的物理内存页面后,要检测是否是第一次访问,如果是则清0使用即可,不需要从调页文件的备份页中读取备份内容。虚拟内存管理器调页时,也会连着旁边的页一起调入,所以写代码的时候应该注意编写紧凑的代码,尽量将会一起访问的数据放在一起。后面对Linux系统的介绍没怎么看orz
第五章讲的是动态内存管理,动态内存主要影响的是堆区的内存。当应用程序需要用同一的机制来控制数据的内存分配情况,并且不想使用系统提供的内存管理机制时,可以通过重写自己的全局operator new/delete来实现。(我觉得我是用不到TAT)一个复杂的C++程序中最容易出现的问题是内存泄漏。即是忘记释放申请的内存,造成程序占用的内存不断上升,系统性能会不断下降,甚至会内存耗尽而导致程序崩溃。有一个解决方法是增加一个引用计数,用于统计有多少个对象指向某块相同的堆区域,刚构造一个对象时就在堆上分配一块空间,并让引用计数值为1,当发生拷贝构造,或者该对象作为右值对另一个对象赋值时就让引用计数加1,而不会去分配堆空间。同样的当某个对象不再指向那块堆区域时,引用计数就减1。对象的析构操作和赋值操作都有可能会使得对象的引用计数减为0,所以在这两个函数里边必须判断什么时候引用计数为0,一旦为0,就释放那块堆区域,不为0就不释放。
第六章讲的是内存池,我是第一次接触到这个概念,书中对内存池的定义大改就是:内存池,顾名思义就是放了很多“内存单元”的“池”。这些“内存单元”大小可以是固定的也可以是可变的,相应的内存池称为“固定内存池”和“可变内存池”。另外,考虑到多线程安全,内存池也可以分为多线程内存池和单线程内存池。书上的是固定大小、单线程内存池。当应用程序需要内存单元时,便从内存池中获取。当内存池中内存单元用完时,内存池就一次性向系统申请很多个内存单元。当内存池中内存单元太多了,就一次性把很多个内存单元返还给系统。使用的时机大概是在一个很大的团队项目里需要的时候再区定义一个,我觉得目前应该不会使用它,就没有仔细的看这部分了。
预计下周能把这本书读完,希望看完能有更多的收获吧。