赞
踩
C++对象内存布局(虚函数表,值类型的子对象直接分配内存单元,引用类型的子对象记录地址),注意不为成员函数分配任何内存空间,代码中的成员函数全部在代码区,且代码中成员函数的调用在编译时就翻译成代码区某个函数首地址的调用,也就是说成员函数本质上跟普通函数没任何区别,只是加了类名这个作用域限制了而已,而且这个限制只在对编译器起作用。所以,C++类在编译后就不起作用了,也可以理解为类只是给编译器看的。
其链接生成的exe文件中就两大区:代码区和全局区。
代码区存放的全部是,也仅仅是各种函数体的语句,此时成员函数和普通函数没任何区别,而且只有函数体的语句,代码区绝无其他任何东西;
全局区存放的是全局性的,也即进程初始化就分配内存的东西,包括全局的常量,全局的变量,全局的对象。简单理解,在编译器过后,C++程序只有两大类实体:函数和变量(对象也是变量)。
Java对象内存布局(值类型的子对象直接分配内存单元,引用类型的子对象记录地址,成员函数的首地址),Java的内存对象布局含有成员函数的首地址,也就是说Java类描述的比较完全,而且java类在编译后仍然存在,运行时类有jvm虚拟机自动管理,所以有反射机制。
其生成的可执行文件包括代码区(java中叫方法区)和全局区(java中叫静态区)和类区。
其中代码区只有函数体的代码,又叫方法区实至名归;
全局区之所以又叫静态区(更应该叫静态变量区)是因为Java是纯面向对象的语言,没有全局的简单类型的变量或对象,也就是说代码中所有定义的实体变量或对象一定都是写在类体中的,不象C++可以写在类体外,其全局变量本质都是静态变量,即写在类体中的静态变量(包括对象)都是全局变量,这些静态的变量在类加载时在静态区分配相应内存;
类区是存放加载的类的信息。
另外一个和C++很重要的不同是,Java的上述三区的内存空间大小不是固定的,都是动态增长的,因为Java中的所有实体(变量,函数)都是写在类体中的,Java运行时用到那个类才动态加载那个类,而加载类的过程中就完成了类中成员函数和静态实体(全局实体)的内存分配过程,其中成员函数动态放入方法区,静态变量动态放入静态区,类放入类区。 至于new一个对象是在栈中执行的代码(函数体内的代码),很简单,不做讨论。
简单理解,在编译器过后,Java可执行程序只有一个空壳,运行时用到哪个类时动态加载那个类,并且动态分配三大实体内存空间:函数、静态变量和类。
PHP等脚本语言内存布局(子对象的字符串形式的名字),就这么简单,无论子对象是变量还是函数,都不区分类型,只记录下名字。由于脚本语言不经过编译,可以理解为运行时先把一个脚本文件全部加载进内存进行简单处理,然后一个语句一个语句的运行(一个完整的语句作为一个运行单位),运行一个语句时简单进行翻译,看看该语句是干什么(无非就是要么读存内存,要么执行函数),然后找到语句的主体(变量名或函数名)后执行即可。
我无从查证脚本语言的对象的内存布局,不过推测就是分为三大区:代码区、DOM形式的树形结构区和变量区。
一个脚本文件加载进内存简单处理后,把所有的函数体放入代码区,把所有定义的变量(包括对象)和函数按照父子关系形成一个DOM形式的树形结构,结构中只存放个个变量的名字或函数的名字(实际上只存放名字和该名字对应的东西分配的内存位置,不区分到底是变量还是函数)。运行时发现是处理变量则从DOM找到这个变量的全名,根据这个名字去变量区(有的语言就是堆区)找到具体内存空间的内容;发现是处理函数,则从DOM找到这个函数的全名,然后根据这个名字在代码区找到函数体体开始运行。
所以脚本语言运行很灵活,例如PHP中有对象值的说法形如:obj.name( ),像这样调用函数,其本质是首先发现这个语句是函数调用功能,语句的主体是obj.name,则从DOM形式结构找到那个name实体(不管类型),然后找到其分配的内存的位置,即函数体,开始执行代码。编译型的语言如Java是绝对不能像这样的,因为对Java来说,obj.name在编译期就翻译成某个具体位置的内存空间了,编译时就绝对确定了,且把obj.name看成一个简单字符串进行编译成具体地址,不会像脚本语言那样进行复杂分析。
【总的来说】:当进程开始时,C++程序是加载整个exe文件,然后分配各大区的内存空间;Java程序是加载第一个类,然后分配各大区的内存空间;PHP等脚本语言是加载整个脚本,然后分配各大区内存空间。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。