在谈堆和栈之前,首先我们先要了解一下Java对内存的分配结构。作为Java程序员大家应该都知道Java的程序都是运行在Java虚拟机上也就是JVM上,程序中所有的变量、实例、方法等都是由JVM在内存上分配空间的。
那么让我们来初步的了解一下Java程序在运行时都会存在哪些内存区域:
1.寄存器:JVM内部虚拟的寄存器跟CPU有关,程序无法控制。
2.栈:用来存放基本数据类型的变量和引用数据类型的实例(实例:某一个类模型new出实体的引用变量)。
3.堆:用来存放程序动态产生的数据,eg:根据类模型new出的实体,需要注意的是我们通过new关键字创建出来的对象在堆内存中只有该对象的成 员变量,而不包括成员方法。因为每一个类的对象都有各自的成员变量存储在各自的堆内存中,但是它们共享该类的成员方法,所以并不是每一次new 都会创建成员方法。
4.常量池:JVM对每个已经加载的类型都会维护一个常量池,常量池就是一个该类型用到的常量的一个有序集合。常量池存在于堆内存区域中。
5.代码段:用来存放冲存储设备中读取的代码片段。
6.数据段:用来存放静态成员,包括静态变量、静态常量、静态方法、静态类。
7.方法区:用来存放所有的函数。
上图简单的描述了Java程序在JVM中运行时的内存分布。接下来我们将了解一下堆和栈的特点。
堆: 1.new出来的对象都有指定的地址值。
2.每个变量都有默认值。eg:int 类型的数组、char类型的数组、Class类型的集合等。
3.当使用完毕后,GC并不会立即对该内存进行清理,GC会在空闲的时候进行扫描该内存是否仍存在引用,若不存在则立即进行回收,若存在则等待下一次的扫描。
栈:通常栈内存存放的是类的实例和基础数据类型(int byte char long double 等),当变量处于该变量的作用于外的时候GC会立即对该内存进行回收。
对于栈内存和堆内存的缺点也比较明显,栈内存在分配空间的时候是固定的不可改变的,而堆内存在分配空间的时候是动态分配空间的。堆内存由于GC并不是立即对废弃的内存进行回收并且GC会采用树的方式进行引用检索,这样就导致了废弃的内存并不会立即回收无端的占用了内存空间。
我想大家应该都看到过这样一个面试题,问"=="和"equals"的区别,那我们通过这个问题来说明堆和栈之间的联系。
String str1 = "abc"; //定义字符串变量str1
String str2 = "abc"; //定义字符串变量str2
String str3 = new String("abc"); //以new的方式定义字符串变量str3
String str4 = new String("abc");//以new的方式定义字符串变量str4
/**
* 那么问题来了
* str1 == str2 ? true:false;
* str2 == str3 ? true:false;
* str3 == str4 ? true:false;
*/
System.out.println("str1 == str2 ? :" + (str1 == str2)); //true
System.out.println("str2 == str3 ? :" + (str2 == str3)); //false
System.out.println("str3 == str4 ? :" + (str3 == str4)); //false
System.out.println("str3.equals(str4) ? :" + str3.equals(str4)); //true