String 引用传递还是值传递 String到底是什么

今天群里讨论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底层对象结构,仅跑了个大概,勿杠。

posted @ 2022-03-07 23:57  方东信  阅读(255)  评论(0编辑  收藏  举报