Java值传递和引用传递实现的原理

Java值传递以及引用传递原理

Java除了8大数据类型之外(int、byte、boolean、long、double、char、short、float )都是引用类型,首先Java所有传参都是值传递


Java的内存分区

我觉得要了解Java的其他方面首先是要了解Java的内存分区,c++分为堆、栈、常量区、静态区、代码区,而Java的内存分区由JVM所决定:首先Java的源文件(.JAVA)会被编译程字节码文件(.Class),而后JVM中的各个类加载器加载字节码文件,然后执行。

在整个执行的过程中,会有一个区域成为Runtime Data Area储存执行期间所要用到的数据和相关的信息,Java在运行期间划分为五个区域,分别是Java栈区PC堆区本地方法栈方法区,为何要这样分区?首先是:

PC

首先是程序计数器,因为代码是运行在虚拟机上的,所以需要由计数器来记录当前的指令,而每一个线程他们当前执行的指令都是不一样的,所以每一个线程都有自己的程序计数器(pc)区。另外,在JVM规范中规定,如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。该区域不会发生内存溢出现像,因为该程序计数器中所存储的数据所占空间不会随着程序运行而改变

Java栈区

与C和C++中的栈类似,先进后出,栈中存放的是一个个栈帧(类似与c++的函数调用栈),每个栈帧都包含一个被调用的方法,栈帧中包括当前的临时变量表(局部变量表)、操作数栈、指向当前方法所属的类的运行时常量池的引用方法的返回地址以及一些其他附加的信息。该区空间的释放以及分配都是由系统自动实施

  局部变量表,顾名思义,就是用来存储放出方法中的局部变量(包括在方法中申明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储他的值,对于应用类型的变量,存的是指向对象的应用。
操作栈帧,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。
指向运行时常量池的引用,因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时的常量池
方法返回地址,当一个方法执行完毕后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法的返回地址。

由于每个线程执行正在执行的方法可能不同,因此每个线程都有一个Java栈,互不干扰。

本地方法栈

作用与栈区相似,但是这里是为本地方法(native method)服务的。

数组以及对象本身都存放于此,当然他们的引用是存放在栈区的。是垃圾管器的主要工作区域,所以说Java的传递都是值传递,只不过对于对象的传递则是传递其引用的值,虽然也在栈上新建了一个变量,但是该变量和形参所指向的对象是同一个。

方法区

方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程池共享的区域。在方法区中,存储每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的的代码等。

在class文件中除了类的字段、方法、接口等描述信息外,还有一项是常量池,用来存储编译期间生成的字面量和符号引用。

在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的常量池就被创建出来。当然并非class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,不懂:比如string的intern方法。//String.intern()方法强制将字符串放入常量池中

常量池是方法区钟存放常量的地方,常量池是为了避免频繁的创建和销毁对象而影响系统性能,实现了常量池中的内容由对象共享。例如字符串常量池,在编译阶段就把所有的字符串文字放到一个常量池中。

  • 节省内存空间:常量池中所有相同的字符串常量被合并,只占用一个空间。
  • 节省运行时间:比较字符串时,==比equals()快。对于两个引用变量,只用==判断引用是否相等,也就可以判断实际值是否相等。

注:==号比较基本数据类型是值比较,但比较引用类型则是引用所指向的地址比较。

public static void main(String[] args) {
String s1 = "liufulei";
String s2 = new String("liufulei");
System.out.println(s1==s2 );//false
System.out.println(s1.equals(s2) );//true 比较引用的数据(重写了equal)
String s3 = s2.intern();
System.out.println(s1==s2);//false
System.out.println( s1.equals(s2));//true
System.out.println(s1==s3);//true
System.out.println(s1.equals(s3));//true
System.out.println(s2==s3 );//false
System.out.println(s2.equals(s3));//true
}

在JVM规范中,没有强制要求方法区必须实现垃圾回收机。

 

 

posted @ 2021-11-16 20:50  码虫垒起代码之城  阅读(111)  评论(0编辑  收藏  举报