今天群里讨论java的String类,顺着大家的讨论 做个简单的总结
String就是引用类型,JAVA也是按值传递 都没毛病,有毛病的是 String这个包装类 的不同之处
先说大众虚拟机中字符串的实现
都说JAVA的一个类型对应着JVM中的一个CPP的类型,我们就来模拟一下,大概长这样。
class JVMString{
//存放字符串的内存地址
char *s;
//记录字符串的长度
int len;
}
现在JVM中String类有了,长度和存放字符串的地址都有了
写一段JAVA代码来推敲
class Test{
void init(){
String s = "方东信";
set(s);
}
void set(String s){
s ="方东信你好!";
}
}
先说init方法
s="方东信"
- 这段代码JVM会new 一个JVMString对象(CPP) 我们假定这个CPP对象叫做A,A的地址是1011
- JVM计算好这几个字符的长度(此处假设3个长度),存到A对象的属性len中,再分配3个内存块,接着把字符串存到这段内存中,再把这段内存的地址存到A对象s指针中
- 此时JVM已经构造好了一个JVMString对象了,实际他就是一个JAVA的对象
再谈set(s)方法
按照传值的原则,JVM会生成一个Arg对象叫X。这个X对象中的ref指针指向A的地址1011 并把X传到set方法中
s ="方东信你好!";
当执行这段代码时,大家有什么想法? 因为底层对应的是CPP的对象,这个对象有个s指针,这个指针指向的内存块只有3个长度,但此出的代码中的字符串 可不是只有3个(想想JAVA中的定长数组,你要改变他的长度,你如何做?)
此时有两种可能的做法:
- JVM会根据当前的字符串长度重新分配一段内存,然后把字符串写进去,再把这个新的地址存到A对象的s指针中.此时的A还是原来的A,只是现在的A的s和之前的A的s指向的地址不同。
这个方案,会造成在set中改变了字符串内容后,外部也改变。
- jvm内部重新new一个JVMString对象 地址为1012. 分配好字符串。把1012这个地址存到X对象的ref指针中。这个时候,1012和1011已经完全不相干了
这个方案,实际就是现在JVM的方案
留给大家思考,为什么JVM不选择第一种方案?
还是给出答案吧
https://zhuanlan.zhihu.com/p/78946350
https://www.jianshu.com/p/91406b649729
特别说明
文中的Arg对象实际是Handler对象,他对JVM中的栈中的对象进行了一次包装,目的是方便GC扫描。
文中并没有严谨表达JVM内部执行流程和JVM底层对象结构,仅跑了个大概,勿杠。