Windows是以图形界面为基础的操作系统。它的前身是 MS-DOC,最初的版本可以在128kb的内存上运行程序,但是现在想要Windows运行流畅至少需要512MB的内存,但通常往往是不够的
很多人认为可以使用虚拟内存来解决内存不足的情况,而虚拟内存确实能够在内存不足的时候提供补充,但是使用虚拟内存的Page In 和 Page Out 通常伴随着低速的磁盘访问,这是一种得不偿失的表现。所以虚拟内存无法从根本上解决内存不足的情况
为了从根本上解决内存不足的情况,要么是增加内存的容量,加内存条;要么优化应用程序,使其尽可能变小。第一种建议往往要需要衡量口袋的银子,所以我们只关注第二种情况
注意:以下的篇幅会涉及到C语言的介绍,是每个程序员(不限语言)都需要知道和了解的知识
一、通过DLL文件实现函数共有
DLL(Dynamic Link Library)文件,是一种 动态链接库 文件,顾名思义,是在程序运行时可以动态加载 Library(函数和数据的集合)的文件。此外,多个应用可以共有同一个DLL文件,而通过共有一个DLL文件则可以达到节约内存的效果
例如,假设我们编写的一个具有处理功能的函数 MyFunc() 。应用A 和 应用B 都需要用到这个函数,然后在各自的应用程序中内置 MyFunc() (这个称为Static Link,静态链接)后同时运行两个应用,内存中就存在了同一个函数的两个程序,这就造成资源浪费
静态链接导致内存利用率下降:
为了改变这一点,使用DLL文件而不是应用程序的执行文件(EXE文件)。因为同一个DLL文件内容在运行时可以被多个应用共有,因此内存中存放函数MyFunc() 的程序就只有一个
使用动态链接DDL改善内存使用:
Windows操作系统其实就是无数个DLL文件的集合体。有些应用在安装时,DLL文件也会被追加,应用程序通过这些DLL文件来运行,既可以节约内存,也可以在不升级EXE文件的情况下,通过升级DLL文件就可以完成更新
二、通过调用 _stdcall 来减少程序文件的大小
通过调用 _stdcoll 来减少文件的方法,是用C语言编写应用时可以利用的高级技巧
_stdcall 是 standard call(标准调用)的缩写。Windows提供的DLL文件内的函数,基本上都是通过 _stdcall 调用方式来完成的,这主要是为了节约内存。另一方面,用C语言编写的程序默认都不是 _stdcall。C语言特有的调用方式称为 C 调用。C语言默认不使用 _stdcall 的原因的是因为 C 语言所对应的函数传入参数是可变的,只有函数调用方才能知道到底有多少个参数,在这种情况下,栈的清理作业便无法进行。不过,在C语言中,如果函数的参数和数量固定的话,指定 _stdcall 是没有任何问题的
C语言和Java最主要的区别之一在于C语言需要人为控制释放内存空间
C语言中,在调用函数后,需要人为执行栈清理指令。把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理出来的操作叫做 栈清理处理
例如下面代码:
//函数调用方 void main(){ int a; a = MyFunc(123,456); } //被调用方 int MyFunc(int a,int b){ ... }
代码中,从main主函数调用到MyFunc() 方法,按照默认的设定,栈的清理处理会附加在main主函数这一方。在同一程序中,有可能会多次调用,导致MyFunc()会进行多次清理,这就会造成内存的浪费
汇编之后的代码如下:
push 1C8h //将参数 456(= 1C8h)存入栈中 push 7Bh //将参数 123(= 7Bh)存入栈中 call @LTD+15(MyFunc)(00401014) //调用 MyFunc函数 add esp,8 //运行栈清理
C 语言通过栈来传递函数的参数,使用 push 是往栈中存入数据的指令,pop 是从栈中取出数据的指令。32位CPU中,1次push指令可以存储4个字节(32位)的数据。上述代码由于进行了两次push操作,所以存储了8字节的数据。通过 call 指令来调用函数,调用完成后,栈中存储的数据就不再需要了。于是就通过add esp,8 这个指令,使存储着栈数据的 esp 寄存器前进8位(设定为指向高8位字节的地址),来进行数据清理。由于栈是在各种情况下都可以利用的内存领域,因此使用完毕后有必要将其恢复到原始状态。上述操作就是执行栈的清理工作。另外,在C语言中,函数的返回值,是通过寄存器而非栈来返回的
栈执行清理工作,在调用方法处执行清理工作和在反复调用方法处执行清理工作不同,使用_stdcall 标准调用的方式称为反复调用方法,在这种情况下执行栈清理开销比较小。
不同方式的调用方式也不同: