现在的位置: 首页 > 综合 > 正文

java 中变量存储位置的区别

2013年02月24日 ⁄ 综合 ⁄ 共 7980字 ⁄ 字号 评论关闭

转自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函数族分配的内存

                                | 未初始化的全局变量   |

                                | 已初始化的全局变量   |

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


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

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


下面

抱歉!评论已关闭.