栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

栈内存 堆内存
基础类型,对象引用(堆内存地址) 由new创建的对象和数组
存取速度快 相对于栈内存较慢
数据大小声明周期必须确定 分配的内存由java虚拟机自动垃圾回收器管理。动态分配内存大小
共享特性(栈中如果有字符串,则直接引用;如果没有,开辟新的空间存入值 每new一次在堆内存中生成一个新的对象。

Java中的数据类型有两种

一、基本类型(primitivetypes)。共有8种即int,short, long, byte, float, double, boolean, char(注意,并没有String的基本类型)。

    这种类型的定义是通过诸如int a= 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a= 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。下面是基本类型的实例化过程(如:int a=100;)

 

二、引用类型。

    包装类数据,如Integer,String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于【堆】中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。


JVM中堆栈的内存分布
:存放一些 基本类型的变量和对象的引用变量。java自动释放掉所分配的空间,该内存空间可以立即被另作他用,存取速度比堆要快。
缺点是,存在栈中的数据大小与生存期必须确定的,缺乏灵活性。栈中主要存放一些基本类型的变量 (int,short,long,byte,float,double,boolean,char)和对象句柄。数据是可以共享的。
:存放由 new创建的对象和数组(可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在 堆内存中的首地址,栈中这个变量就成了数组或对象的引用变量,就相对于一个名字。以后就可以使用栈中的引用 变量来访问堆中的数组或对象。)。是有java虚拟机自动垃圾回收器来管理的。运行时动态分配空间。 因为运行时分配内存,存取速度较慢。


特殊的引用类型:String
    String类型不属于基本类型,但却可以使用基本类型的声明方法,而且String类型的的数据在栈上的存储也有一定的机制(即常量池技术)(编译时的优化方案考虑)。首先举几个例子看一下String的存储机制如下图:
 
    由上图可以看出:String类型的常量字符串都存储在常量池中,而new String()的引用变量的内容存储在堆上。上图中的a、b、c、f是编译器能够确定的字符串,直接存储在常量池中,其中f比较特殊虽然使用了+,但由于编译器的优化之后也可以唯一性确定其最终字符串(所谓唯一性确定即是在编译时候即可确定其最终值,而非等到运行时确定的。若上图的a有final修饰,则e也能编译时候唯一确定其值,那么其值存储在常量池中),所以也存储在常量池中;而对于包含变量的或者new出来的对象,则是在堆上开辟内存存储其值,如上图的d、e、g。
       String类型的声明可以用两种方式:String str="abc";和String str=new String("abc");创建了两个对象,而两个引用分别指向不同的两个对象,前者指向存储在常量池中对象,而后者则指向存储在堆上的对象。采用String str="abc";的方式的内部工作原理:编译器会先查询常量池中是否有存放“abc”的地址,如果有则把该地址赋值给str;如果没有,则会在常量池中新开辟一块地址存放“abc”,然后把该地址赋值给str。而采用String str=new String("abc");的方式则是直接在堆上开辟一块新地址存放“abc”,然后把堆上的地址赋值给栈上的变量str。
    注: String类被设计成为不可改变(immutable)的类。如果要改变其值,可以,但JVM在运行时根据新值悄悄创建了一个新对象,然后将这个对象的地址返回给原来类的引用。
基于上图我们可以用==来判断String中地址的变化过程如下
    String a = "a";
    String b = "b";
    String c = "ab";
    String d = a+b;
    String e = a+"b";
    String f = "a"+"b";
    String g = new String("ab");
    System.out.println(c==d);       //false
    System.out.println(c.equals(d));//true
    System.out.println(c==e);       //false
    System.out.println(c.equals(e));//true
    System.out.println(c==f);       //true
    System.out.println(c.equals(f));//true
    System.out.println(c==g);       //false
    System.out.println(c.equals(g));//true
    System.out.println(e==g);       //false
    System.out.println(e.equals(g));//true

编译器对String类型的优化方案描述:
    (1)优先搜索赋值。对String类型赋值一个常量时候(如String str=“abc”),编译器先搜索常量池中的地址是否存在存储“abc”的地址,有则直接赋值,无则创建后赋值。
    (2)对确定的优先编译赋值。JVM会优先优化它能优化的部分,对于编译时不能确定的变量则不会进行优化。如String str=“a”+“b”+"c",由于常量"a","b","c"编译时候即可确定其值,因此在编译时候直接就变成了String str="abc"参与运算(同理程序中出现int i=3*4+120时,并不是在实际运行时再计算,而是在编译时候直接变成了i=132);但对于String  a="ab";String b=a+"c";时候,编译String b=a+"c"时候,由于编译器不确定变量a的值,在此不会做优化。
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐