问题现象
首先看一个现象,最近在嵌入式项目开发中发现的,下面是设备的内存总量及使用:
总量是24M左右,项目主程序大小1M不到,但是在默认的系统环境设置下,程序运行起来后的top看起来是这样:
VSZ的大小是221MB,所以计算出来的内存使用百分比是935.4% = 221MB/24MB.VSZ表示程序使用的总虚拟内存空间大小。在很久之前也曾遇到过同样的现象,只是当时没有去深入了解为什么。刚开始发现这个221MB时,非常地吃惊,无论如何也想不通为什么1M大小不到的程序会需要使用到200M以上的内存空间。
现象分析
程序是一个多线程的程序,而且有不少的线程是由线程再次创建的,系统环境是linux2.6.32的内核。通过对其它单进程的VSZ大小观察,发现VSZ的大小好像与程序使用的线程数目成正比关系。因此想到可以通过使用Posix Pthread库的pthread_attr_setstacksize接口来修改线程栈的大小,于是将20多个线程的栈的大小修改为512KB,虽然有点麻烦,但是再次运行,VSZ的确大幅地减少为30MB左右。
在分析解决问题的过程中,了解到另一个影响应用程序运行栈大小的系统设置:ulimit -s。通过这个命令可以查看系统默认的栈大小以及修改应用运行时的栈大小,默认的8192KB。这里再次分析上面的现象。linux系统中使用clone机制来实现线程,实际上线程就是一个轻量的进程,因此其栈大小依然是遵循系统的ulimit设置来配置的。所以20多个线程的程序在默认8M的栈大小设置下,会使用到200M左右的虚拟内存空间,包括程序的所有栈空间以及数据内存、堆内存和代码内存。
那么,就可以通过ulimit -s命令修改默认的栈大小,从而达到与调用pthread_attr_setstacksize接口一样的目的和效果。使用ulimit -s 512后,主程序使用的VSZ降低为25M左右,这是因为主线程使用的栈大小也被降低。
但是使用ulimit的一个后果就是它会影响到同一环境(同一shell或者终端)下后续启动的所有程序,如果修改成启动时设置的话就会影响到整个系统,这显然不是想要的。有两个方法可以能消除这个影响:
- 为需要修改栈大小的程序单独编写一个shell脚本,在程序启动前调用ulimit -s。因为子shell的环境不会影响到父shell,所以设置不会改变外部环境。
- 在程序运行前执行ulimit -s修改需要的栈大小,在程序运行后再次执行ulimit -s修改回原来的栈大小。
PS:虽然降低了程序使用的虚拟内存的大小,但是我还是有一个很大的疑问:
程序使用200M多的虚拟内存和使用20M多的虚拟内存,运行效果没有什么变化,好像没有带来什么有用的性能改善。我能想要的“好处”就是系统在进行地址转换和页面管理时会高效一点,但难道不应该有一些更重要的性能提升吗,不然除了让top内容中的VSZ和%MEM栏更好看合理点外,没必要去费精力调整?期待有人能帮忙解惑。