您的位置:首页 > 编程语言 > Java开发

Java栈和堆的区别

2016-02-14 15:24 423 查看
前半部是自己的一些小结 后半部分是转载的

在Java中,我们会在乎两种区域,一个是栈,另外一个是堆,什么是栈,什么是堆?

栈(stack) : 方法调用及变量的生存空间

堆(heap) : 对象的生存空间

黄色是栈 蓝色是堆

方法是堆在一起的:

public void doStuff(){
boolean b = true;
go(4);
}
public void go(int x){
int z = x + 24;
crazy();
}
public void crazy(){
char c = 'a';
}


在这个代码段中,我们要是在某段程序中调用doStuff() 这个方法就被放在stack最上方的栈块中

doStuff() b

dostuff()调用go() ,go()就被放在栈顶

go() x y

doStuff() b

go()调用crazy(),使得crazy()现在处于栈顶

crazy() c

go() x y

doStuff() b

当crazy()执行完成后,它的堆栈块被释放掉,执行就回到了go()

go() x y

doStuff() b

就这样以此类推,直到doStuff()执行完成

对象存在于堆中
不论对象是否声明或创建,如果局部变量是个对给对象的引用,只有变量本身会放在栈上
对象本身只会存在于堆中

public class StackRef{
public void foof(){
barf();
}

public void barf(){
Duck d = new Duck(24);
}
}


黄色是栈 蓝色是堆

barf() d -------->
Duck对象
foof()

在这里,我们就要延伸一下实例变量与局部变量在堆栈中的区别

实例变量:
实例变量是被声明在类,而不是方法里面,他们代表每个独立对象的“字段”;
实例变量存在于所属的对象中

public class Duck{
int size; // 每个Duck对象都会有独立的Size
}

局部变量:
局部变量和方法的参数都是被声明在方法中。它们是暂时的,且生命周期只限于方法被放在栈上的这段时间

public void foo(int x){
int i = x + 3;
boolean b = true; // 参数x和变量i 、b都是局部变量
}

局部变量生存在栈上,实例变量存在于对象所属的堆空间上

实例变量要是primitive主数据类型,则Java会依据primitive主数据类型大小为该实例变量留下空间
int需要32位 long需要64位,以此类推。

要是实例变量是对象呢?

1. 如果有声明变量,没有给它赋值,只会留下变量的空间:
private Dog dog;

2.引用变量被赋值一个新的Dog对象才会在堆上占有空间
private Dog dog = new Dog();

原著博客Java中堆和栈的区别

在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。

当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

堆内存用来存放由new创建的对象和数组。

在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。

在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。

引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象。

java中变量在内存中的分配

1、类变量(static修饰的变量):在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期--一直持续到整个"系统"关闭

2、实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的"物理位置"。 实例变量的生命周期--当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存

3、局部变量:局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放

附:java的内存机制

Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

  堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new
产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。

  这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: