昨天看了一篇关于《Java后端程序员1年工作经验总结》的文章,其中有一段关于String和StringBuffer的描述,对于执行结果仍然把握不准,趁此机会也总结了下JVM内存模型。
1、JVM运行时数据区域
关于JVM内存模型之前也了解过一些,也是看过就忘,好记性比如烂笔头,记下来吧。参考此文章http://chenzhou123520.iteye.com/blog/1585224
图1 JVM运行时数据区域
JVM Stack 异常情况:
StackOverflowError:当线程请求分配的栈容量超过JVM允许的最大容量时抛出
package com.xtli.controller;
public class StringTest {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "world";
System.out.println(s1+"---"+s2);//1:hello---world
change(s1,s2);
System.out.println(s1+"---"+s2);//3:hello---world
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("world");
System.out.println(sb1+"---"+sb2);//4:hello---world
change(sb1,sb2);
System.out.println(sb1+"---"+sb2);//6:hello---worldworld
}
public static void change(String s1, String s2) {
s1 = s2;
s2 = s1+s2;
System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld
}
public static void change(StringBuffer sb1, StringBuffer sb2) {
sb1 = sb2;
sb2.append(sb1);
System.out.println("change(sb1,sb2)---"+sb1+"---"+sb2);//5:change(sb1,sb2)---worldworld---worldworld
}
}
对以上代码进行分析说明,如下
public class StringTest {
public static void main(String[] args) {
//在main方法的栈中创建引用s1和引用s2,此引用s1和引用s2存放在栈(main方法的栈)中;编译时,在常量池中创建两个常量"hello"和"world",s1和s2分别
//指向两个常量
String s1 = "hello";
String s2 = "world";
System.out.println(s1+"---"+s2);//1:hello---world
change(s1,s2);//引用s1和s2作为参数传递到change方法中
//change方法中的引用s1,s2和main方法中的引用s1,s2存放地址并不同,以下输出的是main方法栈中的s1和s2,并没有发生变化,故代码3有以下输出
System.out.println(s1+"---"+s2);//3:hello---world
//以下两行代码将会在main方法栈中创建引用sb1和sb2,并在堆内存中创建两个对象"hello"和"world",sb1和sb2分别指向两个对象
StringBuffer sb1 = new StringBuffer("hello");
StringBuffer sb2 = new StringBuffer("world");
System.out.println(sb1+"---"+sb2);//4:hello---world
change(sb1,sb2);//引用sb1和sb2作为参数传递到change方法中
//main方法中的sb1所指向的堆内存地址未发生变化,故仍为"hello",而change(sb1,sb2)方法改变了main方法中sb2所指向的堆内存地址的内容,故代码6有以下输出
System.out.println(sb1+"---"+sb2);//6:hello---worldworld
}
public static void change(String s1, String s2) {//在change方法的栈中创建引用s1和s2,并指向常量池中的常量
s1 = s2;//将引用s1指向s2的常量池中的"world"
s2 = s1+s2;//在堆内存中创建"worldworld"对象,并将s2指向此堆内存地址
System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld
}
public static void change(StringBuffer sb1, StringBuffer sb2) {//在change方法的栈(和上面的change方法栈不同)中创建引用sb1和sb2,并指向main方法栈中sb1和sb2所指向的对象
sb1 = sb2;//将引用sb1指向sb2所引用的对象"world"
sb2.append(sb1);//引用sb2所指向的对象发生变化,变为"worldworld",注意此时外部main方法中的sb2和此方法中的sb1均指向此堆内存地址,
//此地址内容发生变化后,外部main方法中的sb2指向的内容也跟着变化
System.out.println("change(sb1,sb2)---"+sb1+"---"+sb2);//5:change(sb1,sb2)---worldworld---worldworld
}
}
为了进一步说明change(String s1, String s2)中的结果,可以进行以下验证。
public static void change(String s1, String s2) {
String s= "world";
String ss= "worldworld";
s1 = s2;
System.out.println(s==s1);//输出true
s2 = s1+s2;
System.out.println(ss==s2);//输出false
System.out.println("change(s1,s2)---"+s1+"---"+s2);//2:change(s1,s2)---world---worldworld
}