您的位置:首页 > 职场人生

黑马程序员——java内存分配之堆、栈、常量池

2014-07-15 13:30 211 查看

这几天看到有些小伙伴问这方面的问题

例如①:

int x=1;

Integer y = 1;

Integer z = new Integer(1);

System.out.println(x==y);

System.out.println(x==z);

System.out.println(y==z);

得到的结果:

true

true

flase

例如②:

String str1 = "a";

String str2 = "b";

String str3 = "ab";

String str4 = str1 + str2;

String str5 = new String("ab");

System.out.println(str5.equals(str3));

System.out.println(str5 == str3);

System.out.println(str5.intern() == str3);

System.out.println(str5.intern() == str4);

得到的结果:

true

false

true

false

下面就先说一下堆、栈、常量池:

  在java的内存中主要分为四个部分:栈,堆,数据区(有人说这就是常量池),代码段(存放所有的方法,被线程共享,对象被实例化的时候,都有自己的成员变量,而成员方法是共享的)

栈:存放所有的基本类型变量和对象的引用变量,栈里的诗句是共享的

堆:存放所有由new产生的对象或数组,

常量池:虚拟机必须为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(string,integer和floating point常量)和对其他类型,字段和方法的符号引用。说白了,就是存放基本类型常量和字符串常量的。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的,对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。

主要用于String和8大基本数据类型(这8个里Float和Double没有用到常量池)的包装类Integer,Long,Short,Character,Byte,Booleans.(包装类的数据范围是-128~127,超过了这个范围,就会在堆中新建这个对象,并不会把这对象存在常量池中了,所以,Integer o = 128;Integer i= 128;o就不等于i了)

接下来说一下上面的各种情况:

int i = 1;基本数据类型的值就存在栈中开辟的内存里,所以,基本数据 类型只在栈中开辟一块内存

Integer i2 = 1;他将基本数据类型自动装箱变为了引用数据类型,这里的1也被存到了常量池中了,他在存之前会先检测一下常量池中有没有1这块内存,如果已经开辟过这块内存了,那么,JVM直接将这块内存的地址赋给i2,如果没有的话,则开辟一块新内存存放1。既然是引用数据类型,那i2就是一个地址了,指向常量池中的某块内存,这是就以有人问了,既然i2是一个地址,那判断i==i2是不就输出false了吗?其实不是这样的,我们平时使用==判断的时候都是相同的数据类型,而这里i是一个基本数据类型,i2是一个引用数据类型,这是JVM会先将Integer拆箱转换为基本数据类型,即int i2 = 1;然后在进行比较,这样i和i2不就一样了嘛

Integer i3 = new Integer(1);一看是new,那他肯定是在堆中强制性的开辟一块内存了,不管堆内存中有没有1这块内存都会开辟。

String i4 = “abc”;这里的i4和i可就不一样了,他不是基本数据类型,而是一个引用数据类型,对于String他比较特殊,因为他也实现了常量池,所以这里的abc也被存到了常量池里了。

String i5 = new String(“abc”);既然new了,那肯定是存在堆内存中了,但是这里有个小陷阱:记得看过一个面试题,他问String i5 = new String(“abc”);是开辟了一块内存还是两块,这里你不管回答一块还是两块都不一定对,因为new String()他会强制性的在堆内存中开辟一块内存,“abc”他也是一个对象啊,JVM会现在常量池中找有没有abc这样的字符串,如果有,直接将这个对象拷贝到堆内存中,这样之开辟一快内存,如果没有,在常量池中新建这个对象,这样的话,开辟两块内存

特别注意的是:常量池是在编译的时候开辟内存,堆是在运行的时候开辟内存

String str1 = "a"; String str2 = "b"; String str3 = "ab"; String str4 = str1 + str2; String str5 = "a"+"b"; System.out.println(str3==str4); System.out.println(str3==(str1+str2)); System.out.println(str3==str5); System.out.println(str3=="a"+"b");

得到的结果是:

false

false

true

true

字符串进行相加的时候,如果都是静态字符串,如“a”+“b”,那么组合起来的字符串存放在常量池中,在存之前先会在常量池中查找,这里不多说;如果字符串相加的时候其中有变量,如str1+“a”,str1+str2等,这样JVM会在堆内存中新建一个对象,并不会在常量池中存放。

下面时候一下String里的intern()方法,这是上面的例子:

String str1 = "a";

String str2 = "b";

String str3 = "ab";

String str4 = str1 + str2;

String str5 = new String("ab");

System.out.println(str5.equals(str3));

System.out.println(str5 == str3);

System.out.println(str5.intern() == str3);

System.out.println(str5.intern() == str4);

得到的结果:

true

false

true

false

在调用str5.intern()方法的时候,这个方法会首先检查字符串池中是否有str5这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。

上面的前两个不多说,第三个str5.intern()会先在常量池中查找str5,这里返回了一个引用,就是str3,所以相等

第四个str5.intern()返回的引用是str3,str4是由两个变量组合起来的,上面已经说过,他是在堆内存中开辟的内存,不存放在常量池里,所以两个不相等。

参考资料:

对java中int和Integer的理解 http://blog.sina.com.cn/s/blog_7f033dcf01017ljx.html
java内存分析全面解析 http://blog.csdn.net/shimiso/article/details/8595564
java中的堆、栈、常量池 http://blog.csdn.net/config_man/article/details/5192059

java string = new string 内存分配问题 字符串常量池栈 堆 http://baidongxu.blog.163.com/blog/static/1747323522011924142226/

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: