计算机系统内存分布

1. 概述

在这里插入图片描述

linux软件系统分为内核和应用程序,我觉得主要使用内存是在应用程序。应用程序是以进程和动态库为单位,内存优化应该是以进程和动态库来进行优化。

  • 代码段: (text)存放代码数据
  • 数据段:(data)存放程序中已初始化的全局变量的一块内存区域,static声明的变量也存在这里
  • bss段:存放程序中未初始化的全局变量的一块内存区域
  • 堆区:(heap)存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或缩减。
  • 栈区:(stack)栈又称堆栈,是用户存放程序临时创建的局部变量

2. 内存优化

linux进程由五段组成,BSS段、数据段、代码段、堆、栈组成。代码段是全系统共享,所以进程内存优化主要是在BSS段、数据段、堆、栈内进行。如下记录常用的优化方案。

2.1 堆段:

在进程中、我们可以调用 mallopt 函数,来调整libc的内存管理先行为。

  • M_TRIM_THRESHOLD 堆顶内存回收阀值,默认值为128K。堆顶空闲内存达到阀值时,调用系统brk,将内存归还给系统。
  • M_TOP_PAD 堆顶保留空间内存的数量,默认值为0
  • M_MMAP_THRESHOLD libc中大块内存阀值。大于该阀值,系统调用mmap申请内存。默认值为128K。

2.2 栈段:

linux系统中进程栈段的内存使用增加后,函数返回不会被释放,但是下次进入函数是可以复用的。所以栈段的内存只会增加不 会减少。对我们来讲,尽量少用递归函数及在函数内分配大块内存。
优化方法:

  • 尽量 避免在栈空间申请大量内存。
  • 尽量避免使用递归函数。

环境变量及参数及优化方法:

  • 新增环境变量是存放在堆区。修改环境变量存在新申请的堆空间,不会释放原内存。
  • 尽量在进程启动行设置好环境变量,这样环境变量可以紧密排列在栈空间。

linux bin文件是ELF的文件类型。
读取ELF文件的内容命令:
readelf -a [binFileName]
strip bin文件 :删除bin文件内的GDB调试信息。删除bin文件的调试信息后,bin文件基本上可以缩小一倍以上。

2.3 数据段:

数据段的优化主要是在.bss和.data。

  • .bss : 保存未初始化或者初始化为0的全局变量和静态变量, bss基本不占用ELF文件空间
  • .data : 保存初始化不为0的全局变量和静态变量

对进程自身数据段优化,影响是有限的。对于动态库数据段做优化,效果是比较明显。

2.3.1 数据段优化:

2.3.1.1 尽量减少全局变量和静态变量

采用nm来列出所有在.data和.bss节的变量,方便我们检查。
查看动态库.data和.bss节数据:
nm --format=sysv libfile | grep -w .data
nm --format=sysv libfile | grep -w .bss
输出对应的列信息:
name value class type size lineSection

2.3.1.2 对于非内置类型的全局变量,尽可能采用类指针来代替。

在main函数之前,运行所有非内置类型的全局变量,一方面会降低启动速度,另一方面会浪费物理内存。
对于静态的非内置类型的静态对象,在第一引用时创建对象。

2.3.1.3 将只读的全局变量,加上const使其转移到代码段。利用代码段共享来节省内存。

对类对象的全局变量,加上const也无法转移到.rodata段。原因是类要运行其构造函数,有可能对成员变量赋值。

2.3.1.4 字符串数组优化

将数组改成用函数switch case返回值。

2.4 代码段:

代码段是整个系统共享。在内存不足时还能够回收。因此,代码段优化的意义不大。
主要有以下几点优化:

  • 1、编译主程序时不要使用-export-dynamic。
  • 2、删除冗余代码
    gcc有编译选择来检测冗余代码,并显示warning信息
    -Wunused
    –Wunreachable-code

2.5 动态库:

动态库技术是为了减少程序的大小,节省空间,提高效率,具有很高的奶油灵活性。由于动态库不是程序的一部分,是需要时载入,其执行代码可能在多个程序间共享。运行期间需要查找动态库,对程序的性能会有所损失。

进程对应动态库加载方法有如下两种:

  • 静态加载
  • 动态加载

动态加载是使用函数dlopen,dlclose。动态调用麻烦,还要关注动态库的生存周期。共享库只有数据段和代码段。

不要在进程中通过extern的方式引用共享库中的全局变量。一旦引用都会占用物理内存,同时还会增加系统的启动时间。

共享库全局变量:

  • 1、全局变量声明在进程中,在共享库中使用,该变量位于进程数据段。
  • 2、全局变量声明在共享库中,在进程中使用,变量会被复制到进程的数据段,同时修改使用该变量的共享库的指向。
  • 3、全局变量在共享库中声明,在共享库中使用,则该变量位于声明它共享库的数据段中。
代码段共享:
gcc编译时必须加-fPIC参数进行编译。不加时,共享库无法真正共享。

共享库内导出函数对代码段影响:
    共享库所定义的函数默认都是导出函数,每增加一个函数就是增加 24+函数名长度(byte)。
    GCC通过--version-script选项来指定我们的导出函数。

动态库优化:
动态库数据段所占内存随着依赖他的进程数量成线性增长,代码段则是系统共享,所以将优化重点放在动态库的数据段。

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值