java 中变量存储位置的区别

转自http://blog.csdn.net/yuan22003/article/details/6822221

1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制. 
2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。) 
3. 堆:存放所有new出来的对象。 
4. 静态域:存放静态成员(static定义的) 
5. 常量池:存放字符串常量和基本类型常量(public static final)。 
6. 非RAM存储:硬盘等永久存储空间 

这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。 
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。 
如以下代码: 

[java]  view plain copy
  1. String s1 = "china";    
  2. String s2 = "china";    
  3. String s3 = "china";    
  4. String ss1 = new String("china");    
  5. String ss2 = new String("china");    
  6. String ss3 = new String("china");    

对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。 
对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。 
如以下代码:

[java]  view plain copy
  1. int i1 = 9;    
  2. int i2 = 9;    
  3. int i3 = 9;     
  4. public static final int INT1 = 9;    
  5. public static final int INT2 = 9;    
  6. public static final int INT3 = 9;   

对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。 
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。 
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。 
如以下代码:

[java]  view plain copy
  1. class BirthDate {    
  2.     private int day;    
  3.     private int month;    
  4.     private int year;        
  5.     public BirthDate(int d, int m, int y) {    
  6.         day = d;     
  7.         month = m;     
  8.         year = y;    
  9.     }    
  10. //    省略get,set方法………    
  11. }    
  12.     
  13. public class Test{    
  14.     public static void main(String args[]){    
  15. int date = 9;    
  16.         Test test = new Test();          
  17.            test.change(date);     
  18.         BirthDate d1= new BirthDate(7,7,1970);           
  19.     }      
  20.     
  21.     public void change1(int i){    
  22.         i = 1234;    
  23.     }    

对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化: 
1. main方法开始执行:int date = 9; 
date局部变量,基础类型,引用和值都存在栈中。 
2. Test test = new Test(); 
test为对象引用,存在栈中,对象(new Test())存在堆中。 
3. test.change(date); 
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。 
4. BirthDate d1= new BirthDate(7,7,1970);  
d1 为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。 day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。 

5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。



final修饰的局部变量是存放在栈中还是在常量池中 

存放在常量池中。首先final关键字对于变量的存储区域是没有任何影响的。jvm规范中,类的静态变量存储在方法区,
实例变量存储在堆区。也就是说static关键字才对变量的存储区域造成影响。final关键字来修饰变量表明了该变量一
旦赋值就无法更改。在Java中你可以这样理解:所有的变量,包括基本类型和引用类型,它们的变量都是存放在栈中
,栈中的每个变量都包含类型、名称、值这些内容,只不过基本类型变量的值为一个具体的值,而引用类型的变量的
值为对象在堆中的地址。
java中的常量池技术,是为了方便快捷地创建某些对象而出现的,当需要一个对象时,就可以从池中取一个出来(如果池中没有则创建一个),则在需要重复创建相等变量时节省了很多时间。常量池其实也就是一个内存空间,不同于使用new关键字创建的对象所在的堆空间


转自:http://www.iteye.com/topic/634530
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.
2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(对象可能在常量池里)(字符串常量对象存放在常量池中。)
3. 堆:存放所有new出来的对象。
4. 静态域:存放静态成员(static定义的)
5. 常量池:存放字符串常量和基本类型常量(public static final)。有时,在嵌入式系统中,常量本身会和其他部分分割离开(由于版权等其他原因),所以在这种情况下,可以选择将其放在ROM中 。
6. 非RAM存储:硬盘等永久存储空间

这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。
对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份
如以下代码:

Java代码  收藏代码
  1. String s1 = "china";
  2. String s2 = "china";
  3. String s3 = "china";
  4. String ss1 = new String("china");
  5. String ss2 = new String("china");
  6. String ss3 = new String("china");


 

这里解释一下黄色这3个箭头,对于通过new产生一个字符串(假设为”china”)时,会先去常量池中查找是否已经有了”china”对象,如果没有则在常量池中创建一个此字符串对象,然后堆中再创建一个常量池中此”china”对象的拷贝对象。这也就是有道面试题:String s = new String(“xyz”);产生几个对象?一个或两个,如果常量池中原来没有”xyz”,就是两个。

对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。
如以下代码:

Java代码  收藏代码
  1. int i1 = 9;
  2. int i2 = 9;
  3. int i3 = 9;
  4. public static final int INT1 = 9;
  5. public static final int INT2 = 9;
  6. public static final int INT3 = 9;



 
对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。
形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。
成员变量存储在堆中的对象里面,由垃圾回收器负责回收。
注意:栈里只有一个9 ,i1,i2,i3 都指向9 。如果 令 i2=7会在栈里生成7 再令i2 指向7。
如以下代码:

Java代码  收藏代码
  1. class BirthDate {
  2. private int day;
  3. private int month;
  4. private int year;
  5. public BirthDate(int d, int m, int y) {
  6. day = d;
  7. month = m;
  8. year = y;
  9. }
  10. 省略get,set方法………
  11. }
  12. public class Test{
  13. public static void main(String args[]){
  14. int date = 9;
  15. Test test = new Test();
  16. test.change(date);
  17. BirthDate d1= new BirthDate(7,7,1970);
  18. }
  19. public void change1(int i){
  20. i = 1234;
  21. }

}

 
对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:
1. main方法开始执行:int date = 9;
date局部变量,基础类型,引用和值都存在栈中
2. Test test = new Test();
test为对象引用,存在栈中,对象(new Test())存在堆中。
3. test.change(date);
i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。
4. BirthDate d1= new BirthDate(7,7,1970);
d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。
5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收
------------------------------------------------------------------------------------------------------------------------------
JVM 中的堆栈
JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
  我们知道,某个线程正在执行的方法称为此线程的当前方法.我们可能不知道,当前方法使用的帧称为当前帧。当线程激活一个Java方法,JVM就会在线程的 Java堆栈里新压入一个帧。这个帧自然成为了当前帧.在此方法执行期间,这个帧将用来保存参数,局部变量,中间计算过程和其他数据.这个帧在这里和编译原理中的活动纪录的概念是差不多的.
  从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。
  每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。

JAVA 堆栈
栈与堆都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。
  Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
  栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量(,int, short, long, byte, float, double, boolean, char)和对象句柄。
  栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
  int a = 3;
  int b = 3;
  编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量
----------------------------------------------------------------------------------------------
另外还有一个疑问:
int [] arr={1,2,3,4}
即没有用new 显示生成的原始类型数组是存放在哪的
是存放在堆还是栈里。
在c++ 里肯定是在栈里(C++ 在堆里生成的一定要手动delete 掉自己回收的,而int a[]={1,3}我们不需手动回收)



局部变量,全局变量,静态变量

局部变量

func()

{

int a;

int b=0;

}

很多书籍中也叫自动变量,它声明在函数块内,作用范围也在函数块内。 不能被同一源文件的其他函数使用,也不能被其他文件中的函数使用。

局部变量存储在栈中。无论局部变量显示初始化(如b),或者未初始化(如a),都只有当定义它们的程序块被调用时(即执行时),才分配空间,声明或定义时并不分配,局部变量不是可执行模块的一部分!!除非显示地对局部变量进行初始化,否则,它们的初始值是不确定的。

全局变量

int a;

int b=0;

func()

{

}

全局变量没有声明在任何一个函数内,作用范围在程序运行始终存在。

能被同一源文件的任何函数使用,也能被其他文件中的函数使用,但要使用extern关键字。

全局变量存储在数据段中。全局变量显示初始化时(如b),或者未初始化时(如a),在程序映像中有不同的分区:已初始化的全局变量是可执行模块的一部分。未初始化的全局变量则不是可执行模块的一部分,只有当定义它们的程序被调用时(即执行时),才分配空间,声明或定义时并不分配。未初始化的全局变量在运行时被初始化为0。

静态变量

用static关键字定义的变量,与局部变量相比, static局部变量有三点不同:
1. 存储空间分配不同
auto类型分配在栈上,属于动态存储类别,占动态存储区空间,函数调用结束后自动释放, 而static分配在静态存储区,在程序整个运行期间都不释放,两者之间的作用域相同,但生存期不同。
2. static局部变量在所处模块在初次运行时进行初始化工作,且只操作一次。
3. 对于局部静态变量,如果不赋初值,编译期会自动赋初值0或空字符,而auto类型的初值是不确定的。(对于C++中的class对象例外, class的对象实例如果不初始化, 则会自动调用默认构造函数, 不管是否是static类型)

静态全局变量用来表示不能被其它文件访问的全局变量和函数。为了限制全局变量/函数的作用域,函数或变量前加static使得函数成为静态函数。但此处“static”的含义不是指存储方式,而是指对函数的作用域仅局限于本文件(所以又称内部函数)。注意:对于外部(全局)变量,不论是否有static限制,它的存储区域都是在静态存储区,生存期都是全局的。此时的static只是起作用域限制作用,限定作用域在本模块(文件)内部。

静态全局变量与全局变量的差别是:静态全局变量只能被同一源文件中的函数调用,其他文件中的函数不能调用静态全局变量。

主存中的程序映像布局

高端地址(小地址)| 命令行参数和环境变量|    <--argc、argv、环境

                                |                 栈                |     <--函数调用的活动记录(返回地址参数,已保存的寄存器,局部变量)

                               |             ..........             |

                                |                 堆                |    <--用malloc函数族分配的内存

                                | 未初始化的全局变量   |

                                | 已初始化的全局变量   |

低端地址(大地址)|     程序文本                |



局部变量,静态局部变量,全局变量,静态全局变量在内存中的存放区别(转)

     我们先来看内存中的几大区:  内存到底分几个区?


下面有几种网上的理解,我整理一下:

一:

 1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。

 2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由os回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。

 3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放。

 4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放。

 5、程序代码区—存放函数体的二进制代码。 

 二:

 1、栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

 2、堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

 3、自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

 4、全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的c语言中,全局变量又分为初始化的和未初始化的,在c++里面没有这个区分了,他们共同占用同一块内存区。

 5、常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。

 三:

 1、bss是英文block started by symbol的简称,通常是指用来存放程序中未初始化的全局变量的一块内存区域,在程序载入时由内核清0。bss段属于静态内存分配。它的初始值也是由用户自己定义的连接定位文件所确定,用户应该将它定义在可读写的ram区内,源程序中使用malloc分配的内存就是这一块,它不是根据data大小确定,主要由程序中同时分配内存最大值所确定,不过如果超出了范围,也就是分配失败,可以等空间释放之后再分配。

 2、text段是程序代码段,在at91库中是表示程序段的大小,它是由编译器在编译连接时自动计算的,当你在链接定位文件中将该符号放置在代码段后,那么该符号表示的值就是代码段大小,编译连接时,该符号所代表的值会自动代入到源程序中。

 3、data包含静态初始化的数据,所以有初值的全局变量和static变量在data区。段的起始位置也是由连接定位文件所确定,大小在编译连接时自动分配,它和你的程序大小没有关系,但和程序使用到的全局变量,常量数量相关。

 4、stack保存函数的局部变量和参数。是一种“后进先出”(last in first out,lifo)的数据结构,这意味着最后放到栈上的数据,将会是第一个从栈上移走的数据。对于哪些暂时存贮的信息,和不需要长时间保存的信息来说,lifo这种数据结构非常理想。在调用函数或过程后,系统通常会清除栈上保存的局部变量、函数调用信息及其它的信息。栈另外一个重要的特征是,它的地址空间“向下减少”,即当栈上保存的数据越多,栈的地址就越低。栈(stack)的顶部在可读写的ram区的最后。

 

 5、heap保存函数内部动态分配内存,是另外一种用来保存程序信息的数据结构,更准确的说是保存程序的动态变量。堆是“先进先出”(first in first out,fifo)数据结构。它只允许在堆的一端插入数据,在另一端移走数据。堆的地址空间“向上增加”,即当堆上保存的数据越多,堆的地址就越高。

 

 总结(不确定!!!):

 研究这个意义不大,不同编译器,可能行为不同,如果是vc的话,基本上如下:

 1、代码区,是编译器生成的一个exe区段,拥有可读和可执行属性,但是实际上如果不开dep数据执行保护,所有的区段都是可执行的。

 2、所谓的栈区,低地址(小于exe基地址),拥有可读写属性,exe中没有对应的区段,系统加载dll时自动生成,由于内存地址使用方式从大往小减,所以数量有限,尽量不要定义过大的数组变量。 const的局部变量也是放在栈里的,而不是放在常量区。

 3、所谓的堆区,就是malloc和new之类的内存所在区段,拥有可读写属性,exe中没有对应的区段,系统加载dll时自动生成,首先是利用栈区地址下面的区段,也是低地址,当用完了,会自动分配稍微高一点地址(大于exe基地址)。 malloc和new都在这里分配内存。

 4、全局数据区,是编译器生成的一个exe区段,拥有可读写属性,初始和未初始化的全局和静态变量都放在这里。

 5、常量区,是编译器生成的一个exe区段,只有可读属性,比如char s = " hello world" ,这时候" hello world" 就在常量区,由于没有可写属性,所以修改内容会出错,另外全局的const变量也放在常量区里,这和c++程序设计语言里对const变量存放位置是不符合的,因为存储器各有各的差异。

 

局部变量,局部静态变量,全局变量,全局静态变量区别:

局部变量:    栈区
局部静态变量:静态区
全局变量:    静态区的常量区
全局静态变量:静态区

在进行C/C++编程时,需要程序员对内存的了解比较精准。经常需要操作的内存可分为以下几个类别: 
      1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 
       2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 
      3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放 
      4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 
      5、程序代码区—存放函数体的二进制代码。 
以下是一段实际说明的程序代码:

这是一个前辈写的,非常详细 
//main.cpp 
int a = 0; 全局初始化区 
char *p1; 全局未初始化区 
main() 

int b; 栈 
char s[] = "abc"; 栈 
char *p2; 栈 
char *p3 = "123456"; 123456在常量区,p3在栈上。 
static int c =0; 全局(静态)初始化区 
p1 = (char *)malloc(10); 
p2 = (char *)malloc(20); 
分配得来得10和20字节的区域就在堆区。 
strcpy(p1, "123456"); 123456放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。 
}


二、堆和栈的理论知识 


2.1申请方式 

stack: 
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间 
heap: 
需要程序员自己申请,并指明大小,在c中malloc函数 
如p1 = (char *)malloc(10); 
在C++中用new运算符 
如p2 = (char *)malloc(10); 
但是注意p1、p2本身是在栈中的。 
2.2 
申请后系统的响应 


栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 

堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时, 
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 


2.3申请大小的限制 

栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 


2.4申请效率的比较:  www.2cto.com

栈由系统自动分配,速度较快。但程序员是无法控制的。 
堆是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便. 
另外,在WINDOWS下,最好的方式是用VirtualAlloc分配内存,他不是在堆,也不是在栈是直接在进程的地址空间中保留一快内存,虽然用起来最不方便。但是速度, 也最灵活 


2.5堆和栈中的存储内容 

栈: 在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 
当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 
堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容有程序员安排。 


2.6存取效率的比较

char s1[] = "aaaaaaaaaaaaaaa"; 
char *s2 = "bbbbbbbbbbbbbbbbb"; 
aaaaaaaaaaa是在运行时刻赋值的; 
而bbbbbbbbbbb是在编译时就确定的; 
但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。 
比如: 
#include 
void main() 

char a = 1; 
char c[] = "1234567890"; 
char *p ="1234567890"; 
a = c[1]; 
a = p[1]; 
return; 

对应的汇编代码 
10: a = c[1]; 
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh] 
0040106A 88 4D FC mov byte ptr [ebp-4],cl 
11: a = p[1]; 
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h] 
00401070 8A 42 01 mov al,byte ptr [edx+1] 
00401073 88 45 FC mov byte ptr [ebp-4],al 
第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指edx中,在根据edx读取字符,显然慢了

 

小结: 
堆和栈的区别可以用如下的比喻来看出: 
使用栈就象我们去饭馆里吃饭,只管点菜(发出申请)、付钱、和吃(使用),吃饱了就走,不必理会切菜、洗菜等准备工作和洗碗、刷锅等扫尾工作,他的好处是快捷,但是自由度小。 
使用堆就象是自己动手做喜欢吃的菜肴,比较麻烦,但是比较符合自己的口味,而且自由度大。 

堆和栈的区别主要分: 
操作系统方面的堆和栈,如上面说的那些,不多说了。 
还有就是数据结构方面的堆和栈,这些都是不同的概念。这里的堆实际上指的就是(满足堆性质的)优先队列的一种数据结构,第1个元素有最高的优先权;栈实际上就是满足先进后出的性质的数学或数据结构。 
虽然堆栈,堆栈的说法是连起来叫,但是他们还是有很大区别的,连着叫只是由于历史的原因。

 

栈堆
2013-09-04 | 阅: 1   转:1  |   分享 
   修改
1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.   2. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)   3. 堆:存放所有new出来的对象。   4. 静态域:存放静态成员(static定义的)   5. 常量池:存放字符串常量和基本类型常量(public static final)。   6. 非RAM存储:硬盘等永久存储空间   这里我们主要关心栈,堆和常量池,对于栈和常量池中的对象可以共享,对于堆中的对象不可以共享。栈中的数据大小和生命周期是可以确定的,当没有引用指向数据时,这个数据就会消失。堆中的对象的由垃圾回收器负责回收,因此大小和生命周期不需要确定,具有很大的灵活性。   对于字符串:其对象的引用都是存储在栈中的,如果是编译期已经创建好(直接用双引号定义的)的就存储在常量池中,如果是运行期(new出来的)才能确定的就存储在堆中。对于equals相等的字符串,在常量池中永远只有一份,在堆中有多份。   如以下代码:   Java代码   String s1 = "china";   String s2 = "china";   String s3 = "china";   String ss1 = new String("china");   String ss2 = new String("china");   String ss3 = new String("china");      对于基础类型的变量和常量:变量和引用存储在栈中,常量存储在常量池中。   如以下代码:   Java代码   int i1 = 9;   int i2 = 9;   int i3 = 9;   public static final int INT1 = 9;   public static final int INT2 = 9;   public static final int INT3 = 9;      对于成员变量和局部变量:成员变量就是方法外部,类的内部定义的变量;局部变量就是方法或语句块内部定义的变量。局部变量必须初始化。 形式参数是局部变量,局部变量的数据存在于栈内存中。栈内存中的局部变量随着方法的消失而消失。   成员变量存储在堆中的对象里面,由垃圾回收器负责回收。   如以下代码:   Java代码   class BirthDate {   private int day;   private int month;   private int year;   public BirthDate(int d, int m, int y) {   day = d;   month = m;   year = y;   }   省略get,set方法………   }   public class Test{   public static void main(String args[]){   int date = 9;   Test test = new Test();   test.change(date);   BirthDate d1= new BirthDate(7,7,1970);   }   public void change1(int i){   i = 1234;   }      对于以上这段代码,date为局部变量,i,d,m,y都是形参为局部变量,day,month,year为成员变量。下面分析一下代码执行时候的变化:   1. main方法开始执行:int date = 9;   date局部变量,基础类型,引用和值都存在栈中。   2. Test test = new Test();   test为对象引用,存在栈中,对象(new Test())存在堆中。   3. test.change(date);   i为局部变量,引用和值存在栈中。当方法change执行完成后,i就会从栈中消失。   4. BirthDate d1= new BirthDate(7,7,1970);   d1为对象引用,存在栈中,对象(new BirthDate())存在堆中,其中d,m,y为局部变量存储在栈中,且它们的类型为基础类型,因此它们的数据也存储在栈中。day,month,year为成员变量,它们存储在堆中(new BirthDate()里面)。当BirthDate构造方法执行完之后,d,m,y将从栈中消失。   5.main方法执行完之后,date变量,test,d1引用将从栈中消失,new Test(),new BirthDate()将等待垃圾回收。

你可能感兴趣的:(java)