- G++在编译时,为这些非内置类型的全局变量,在.bss节预留了内存空间;同时在.init_array节安排了全局变量的构造函数
- 在程序运行时,在mmap将数据段映射进入内存之后,调用位于.init_array节的全局对象的构造函数,将全局对象创建出来
- 执行main函数内的代码
- C语言编写的进程,在运行时只是通过mmap为其数据段分配一段虚拟内存,只有在实际用到时才会分配物理内存
- C++程序的那些非内置类型的全局变量,由于在main函数之前,需要运行构造函数,为其成员变量赋值,这时候虽然程序还未用到,但它已经开始占用物理内存,并且有些非内置类型的全局对象,可能在进程启动过程中根本用不到,而其仍然占用物理内存,从而造成内存的浪费
优化方案
- 减少一个整型变量不会只减少4B,可能并不会减少内存的使用,也可能减少4KB物理内存(Linux内核中内存分配的最小单位为4KB),是否减少内存的使用主要是看数据段在最后一个页面中所使用的内存大小,如果最后一个页面只用了4B,那么减少一个整型的全局变量,会节省4KB的内存使用;如果最后一个页面不知使用4B,那么减少一个整型的全局变量,并不会节省内存的使用
- 对于执行文件的数据段做优化,影响有限;但如果堆动态库优化,效果会很明显。例如一个动态库被50个进程依赖,那么50个进程在运行时就会存在50个该动态库的数据段,没减少一个整型的全局变量,理论上它将节省4Bx50=200B,假如节约出一个页面,则总共能节省4KBx50=200KB物理内存
优化方法
- 尽可能减少全局变量和静态变量。可以使用nm列出所有在.data和.bss节的变量方便查看
nm --format=sysv yourlib|grep -w .data nm --format=sysv yourlib|grep -w .bss
-
对于非内置类型的全局变量,尽可能使用全局对象指针来代替。进程会进入main函数之前,运行所有非内置类型的全局变量的构造函数,一方面会降低进程启动速度,另一方面即使没有使用该全局变量,其也已经开始占用物理内存,造成浪费
//优化前 class Myclass; Myclass obj; int main(){ //.... } //优化后 class Myclass; Myclass *pobj; Myclass *getMyobj(){ if(pobj==NULL) pobj = new Myclass; return pobj; } int main(){ //..... }
好处在于:全局对象obj改为指针后,其不需要运行构造函数,优化前对象obj是在main函数之前就构造出来;优化后是在main函数之后,用到的时候才构造出来。如果该全局对象不是启动时需要,那么此优化方式就可以起到节约内存目的
-
将只读的全局变量加上const,从而使其转移到代码段,利用代码段是系统共享的特性达到节约内存目的,但是非内置类型此处无效,因为const只是表明对象构造完成后无效
-
关于字符串数组的优化,每个字符串的长度都不一样
-
数据段优化主要思路是将其从数据段移动到代码段,利用代码段在系统内共享的特点节省内存使用