首页 > 系统相关 >节约内存的编程方式

节约内存的编程方式

时间:2023-01-25 19:23:04浏览次数:57  
标签:文件 节约 函数 Windows 编程 DLL 内存 应用

以图形用户界面(GUI, Graphical User Interface)为基础的Windows,可以说是一个巨大的操作系统。Windows的前身是MS-DOS操作系统,最初版本可以在128KB左右的内存上运行,而想要Windows流畅运行的话,至少需要512MB的内存。而且,由于Windows具有多任务功能,在巨大的Windows操作系统中可以同时运行多个应用,因此,即使是512MB的内存,有时也无法保证流畅运行。Windows操作系统经常为内存不足所困。

像Windows这样,窗口的菜单及图表等都可以进行可视化操作的方式称为图形用户界面。Windows的前身MS-DOS操作系统,是由键盘输入命令来进行操作的CLI(命令行界面)。

 许多人可能会认为,通过借助磁盘虚拟内存就可以解决内存不足的问题。而虚拟内存也确实能避免因内存不足导致的应用无法启动。不过,由于使用虚拟内存时发生的Page In和Page Out往往伴随着低速的磁盘访问,因此在这个过程中应用的运行会变得迟钝起来。想必大家也都有过在操作应用的过程中硬盘访问灯一直亮着(这时正在进行Page In和Page Out),导致应用一时无法操作的不愉快经历吧。也就是说,虚拟内存无法彻底解决内存不足的问题。

为了从根本上解决内存不足的问题,需要增加内存的容量,或者尽量把运行的应用文件变小。接下来会向大家介绍两个把应用文件变小的编程方法。虽然增加内存容量更为便捷,但是花费也高。

(1)通过DLL文件实现函数共有
DLL文件:是在程序运行时可以动态加载Library(函数和数据的集合)的文件。此外,还有一个需要大家注意的地方,那就是多个应用可以共有同一个DLL文件。而通过共有同一个DLL文件则可以达到节约内存的效果。
例如,假设我们编写了一个具有某些处理功能的函数MyFunc()。应用A和应用B都会使用这个函数。在各个应用的运行文件中内置函数 MyFunc()(这个称为Static Link,静态链接)后同时运行这两个应用,内存中就存在了具有同一函数的两个程序。但这会导致内存的利用效率降低。所以,有两个同样的函数,还是有点浪费,如下图:

 

 那么,如果函数MyFunc()是独立的DLL文件而不是应用的执行文件(EXE文件),那结果会怎样呢?由于同一个DLL文件的内容在运行时可以被多个应用共有,因此内存中存在的函数MyFunc()的程序就只有1个。这样一来,内存的利用效率也就提高了。

 

 Windows中,可以执行的应用文件的扩展名是.exe,这样的文件就称为EXE文件。exe是executable(可执行)的略写。另一方面,DLL文件的扩展名为.dll。

Windows的操作系统本身也是多个DLL文件的集合体。有时在安装新应用时,DLL文件也会被追加。应用则会通过利用这些DLL文件的功能来运行。像这样,之所以要利用多个DLL文件,其中一个原因就是可以节约内存。而且DLL文件还有一个优点就是,在不变更EXE文件的情况下,只通过升级DLL文件就可以更新。
(2)通过调用_stdcall来减小程序文件的大小
通过调用_stdcall”来减小程序文件的方法,是用C语言编写应用时可以利用的高级技巧。不过,这一思路应该也可以应用在其他编程语言中。
_stdcall是standard call(标准调用)的略称。Windwos提供的DLL文件内的函数,基本上都是_stdcall调用方式。这主要是为了节约内存。另一方面,用C语言编写的程序内的函数,默认设置都不是_stdcall。C语言特有的调用方式称为C调用。C语言之所以默认不使用_stdcall,是因为C语言所对应的函数的传入参数是可变的(可以设定任意参数),只有函数调用方才能知道到底有多少个参数,而这种情况下,栈的清理作业便无法进行。不过,在C语言中,如果函数的参数数量固定的话,指定_stdeall是没有任何问题的。
C语言中,在调用函数后,需要执行栈清理处理指令。

栈清理处理是指把不需要的数据从接收和传递函数的参数时使用的内存上的栈区域中清理出去。该命令不是程序记述的,而是在程序编译时由编译器自动附加到程序中的。编译器默认将该处理附加在函数调用方。
例如,在代码清单5-1中,从函数 main(中调用了函数 MyFunc0。按照默认设定,栈的清理处理会附加在函数main)这一方。在同一个程序中,同样的函数可能会被多次反复调用。而如果是同样的函数,栈清理处理的内容也是一样的。由于该处理是在调用函数一方,因此就会导致同一处理被反复进行。这就造成了内存的浪费。

 

 虽然通过调查编译器生成的机器语言执行文件就可以得知栈清理的处理内容,不过鉴于原始的机器语言不太容易理解,所以这里我们用汇编语言的代码清单将其显示了出来。将代码清单5-1中调用函数MyFunc()的部分用汇编语言来表示,就如代码清单5-2所示。最后1行的处理就是清理处理。

 

 C语言通过栈来传递函数的参数。push是往栈中存入数据的指令。32位CPU中,1次push指令可以存储4个字节的数据。代码清单5-2中,由于使用了两次push指令把两个参数(456和123)存入到了栈中,因此总的来说就是存储了8字节的数据。通过call指令调用函数MyFunc()后,栈中存储的数据就不再需要了。于是这时就通过addesp,8这个指令,使存储着栈数据的esp寄存器前进8位(设定为指向高8位字节地址),来进行数据清理。由于栈是在各种情况下都可以再利用的内存领域,因此使用完毕后有必要将其恢复到原状态。上述这些操作就是栈的清理处理。另外,在C语言中,函数的返回值,是通过寄存器而非栈来返回的。

栈清理处理,比起在函数调用方进行,在反复被调用的函数一方进行时,程序整体要小一些。这时所使用的就是_stdcall。在函数前加上_stdcall,就可以把栈清理处理变为在被调用函数一方进行。把代码清单
5-1中的 int MyFunc(int a, int b)部分转成 int_stdcall MyFunc(int a, int b)进行再编译后,和代码清单5-2中add esp,8同样的处理就会在函数MyFunc)一方执行。虽然该处理只能节约3个字节(add esp,8是机器语

的3个字节)的程序大小,不过在整个程序中还是有效果的(图5-7)。

 

标签:文件,节约,函数,Windows,编程,DLL,内存,应用
From: https://www.cnblogs.com/2674308160-lucky/p/17067153.html

相关文章

  • 学习ASP.NET Core Blazor编程系列二十二——登录(1)
    学习ASP.NETCoreBlazor编程系列文章之目录学习ASP.NETCoreBlazor编程系列一——综述学习ASP.NETCoreBlazor编程系列二——第一个Blazor应用程序(上)学习A......
  • 虚拟内存把磁盘作为部分内存来使用
    接下来说一下虚拟内存虚拟内存是指把磁盘的一部分作为假想的内存来使用。这与磁盘缓存是假想的磁盘(实际上是内存)相对,虚拟内存是假想的内存(实际上是磁盘)。通过借助虚拟......
  • 不读入内存就无法运行
    1.存储程序方式指的是什么?在存储装置中保存程序,并逐一运行的方式2.通过使用内存来提高磁盘访问速度的机制称为什么?DiskCache(磁盘缓存)3.把磁盘的一部分作为假想内存来......
  • Day14 - 网络编程
    1.IP地址IP地址的概念IP地址就是标识网络中设备的一个地址,好比现实生活中的家庭地址。网络中的设备效果图:IP地址的表现形式说明:IP地址分为两类:IPv4......
  • Day13 - 多任务编程【线程】
    1.线程介绍线程也是实现多任务的一种方式一个程序在执行时会对应一个主进程,主进程中会有一个主线程通过主线程手动产生的线程称为子线程进程是最小资源分配单位线程......
  • Day12 - 多任务编程【进程】
    0.多任务的概念多任务是指在同一时间内执行多个任务,例如:现在电脑安装的操作系统都是多任务操作系统,可以同时运行着多个软件。1.多任务介绍多任务为提高程序的执行效......
  • 适合编程初学者的开源云笔记系统(Python版)
    目标为编程初学者打造入门学习项目,使用各种主流编程语言来实现。让想学编程的,一个都不落下。上述基本涵盖了当前编程开发所有主流语言。左侧为前端版本:安卓、iOS、鸿蒙......
  • 适合编程初学者的开源云笔记系统(PHP版)
    目标为编程初学者打造入门学习项目,使用各种主流编程语言来实现。让想学编程的,一个都不落下。上述基本涵盖了当前编程开发所有主流语言。左侧为前端版本:安卓、iOS、鸿蒙......
  • 适合编程初学者的开源云笔记系统(NodeJS版)
    目标为编程初学者打造入门学习项目,使用各种主流编程语言来实现。让想学编程的,一个都不落下。上述基本涵盖了当前编程开发所有主流语言。左侧为前端版本:安卓、iOS、鸿蒙......
  • 适合编程初学者的开源云笔记系统(Go语言版)
    目标为编程初学者打造入门学习项目,使用各种主流编程语言来实现。让想学编程的,一个都不落下。上述基本涵盖了当前编程开发所有主流语言。左侧为前端版本:安卓、iOS、鸿蒙......