1、概述:
本篇博文主要谈谈c/c++、oc、c#、java的内存模型,主要是对平时自己所学所思所悟加以总结,本人早期从事c/c++开发,后又做过c#和java相关的开发,也做过跨平台跨语言的开发,最多同时在windows、linux、unix、android、ios上开发,同时使用汇编、c/c++/c++11(其实可以完全使用C++11,但嵌入式设备不支持)、oc、c#、java、js各类语言开发过。
下面以java为例引入话题,如:
public class Sd {
private static int a;
private int b;
public int f(int c){
int d = 0;
return d;
}
}
看了此篇博文你将知道,a、b、c、d和返回值,到底在哪些内存区域,另外我将会从汇编语言开始讲解什么是栈区、堆区、全局数据区、代码区,等所谓的区(其实只有栈区、数据区、和代码区),java搞了一个jvm、c#搞一个.net就创区了,哧哧!操作系统更无耻,它要管理进程调度、资源分配,所以堆区(与JVM堆不是一会事)又出来了。
另外我也会分析为什么汇编、c/c++、oc这些语言的效率,远远高于java和c#这些半脚本语言,c#又高于java,c/c++又高于oc。
半脚本是我取的,其它地方看不到,何故取此名?1、非脚本又要解析才能形成本地机器指令运行,2、是脚本它又非文本,正因为这样不伦不类,才受广大码农的喜爱,后续会说原因。
看此篇博文,你必需了解多语言,并且有汇编语言和一些计算机组成原理的基础。
2、8086汇编基础:
此节以8086来介绍程序运行原理,看此节需明白,8086运行是独占的,也就是说整台电脑同一时刻只有一个进程在运行,连操作系统都没有(其实有bios)。
写过汇编的人都知道,我们还是需要将mov ax,4E32,这样的指令编译成可执行文件才能运行,下面就来说说可执文件是如何加载运行的?
可执行文件被加载到内存后,cpu将会通过指令总线、地址总线访问一个固定的位置,获取值对cs、ss、ds赋值,完成内存地址的分割,cs、sp和ds分别对应指令段、栈段、和数据段。
分段后,指令段将会被cpu认为是可执行的指令,栈段和数据段将会被cpu认为是数据区,栈段后进先出的访问模式,是高级语言函数方法的形参、局部变量、返回值的灵魂,无它,我们编码时,全部只能使用数据区,也就是说程序还未运行前就要布局内存空间,函数方法相互调用、递归调用将很难实现,for、while、do while这些循环很难实现,最终我们编码难度将大大增大。
看到这里你需明白,假如:我们内存是4G,你可以理解前1G是指令段,用于存储执行指令。中间2G是栈段用于存储函数方法调用压栈的形参、局部变量、返回值。最后1G数据区,用来存全局数据,如:在C++语言中就是*.cpp文件中,方法体外用static定义的变量,如:staitc int a = 10;
最后用栈溢出,再详细说栈区的工作原理,先看一段c代码:
int add(int a){
if(0==a){ return a; }
return a += add(a-1);
}
int main(int argc,char *argv[]){
printf("sum argc value is:%d", add(argc));
return 0;
}
这是一段典型的递归调用,add每调用一次将会压栈,栈区使用内存将会往上增,argc值越大压栈越高,如图:
用VS将上面C++代码反编成汇编,如:
6: int main(int argc,char *argv[])
7: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
0