黑马程序员技术交流社区

标题: java内存分配之堆、栈、常量池 [打印本页]

作者: 001可望成功    时间: 2014-7-12 14:41
标题: java内存分配之堆、栈、常量池
本帖最后由 001可望成功 于 2014-7-14 10:39 编辑

这几天看到有些小伙伴问这方面的问题
例如①:
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的内存中主要分为四个部分:栈,堆,数据区(有人说这就是常量池),代码段(存放所有的方法,被线程共享,对象被实例化的时候,都有自己的成员变量,而成员方法是共享的)
接下来说一下上面的各种情况:

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/






作者: 卞新浩    时间: 2014-7-12 21:53
讲的号仔细  值得收藏
作者: 渴望学习    时间: 2014-7-12 22:36
顶起!!!
作者: zxdanshui    时间: 2014-7-12 22:39
学习了  
作者: 布鲁图斯    时间: 2014-7-12 23:35
:handshake
作者: lancerjs    时间: 2014-7-12 23:43
lz讲的很详细,学习了
作者: 钱程永    时间: 2014-7-13 00:45
好牛啊!顶起
作者: cheye0207    时间: 2014-7-13 02:34
呵呵,不错,受教了
作者: mytongyong    时间: 2014-7-13 06:11
受教了

作者: 为爱编程    时间: 2014-7-13 10:37
牛人遍天下           
作者: sk120110    时间: 2014-7-13 11:00
讲得好,学习
作者: 江祖贇    时间: 2014-7-13 11:09
不错的整理,收下了,谢咯
作者: 西地那非    时间: 2014-7-13 12:39
学习了,讲的很透彻。
作者: 001可望成功    时间: 2014-7-13 16:38
本帖最后由 001可望成功 于 2014-7-14 10:40 编辑

评论说的很正确,那里有点误导大家了

对于数据区这个,看《java开发实践经典》上是这么写的,这块不是很清楚了,但是看网上的资料,有的人说他就是常量池,


作者: 马到成功2014    时间: 2014-7-13 17:32
收藏了 谢谢
作者: 小七    时间: 2014-7-13 20:54
谢谢分享
作者: 001可望成功    时间: 2014-7-14 22:54
看来帮助了很多的小伙伴啊,谢谢大家的支持
作者: 依然超级赛亚人    时间: 2014-7-14 23:13
恩,说得好详细,受教了,顶一个!
作者: 周峰峰    时间: 2014-7-17 17:01
说的很详细!




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2