jvm(31),理解升级----通过jvm内存模型深入理解值传递和引用传递两种方式

qingruihappy qingruihappy     2023-01-02     274

关键词:

值传递和引用传递分析

Java中数据类型分为两大类:基本类型和引用类型(也就是对象类型)。

基本类型:boolean、char、byte、short、int、long、float、double

引用类型:类、接口、数组

因此,变量类型也可分为两大类:基本类型和引用类型。

在分析值传递和引用传递之前,建议了解下以上变量类型在Java内存管理模型中的位置,如果对此有所了解,将更加有助于理解两种传递的方式^_^

在Java内存中,基本类型变量存储在Java栈(VM Stack)中,引用变量存储在堆(Heap)中,模型如下:

技术分享图片

值传递和引用传递的定义:

这里要用实际参数和形式参数的概念来帮助理解

值传递:方法调用时,实际参数把它的值传递给对应的形式参数,函数接收的是原始值的一个 copy,此时内存中存在两个相等的基本类型,即实际参数和形式参数,后面方法中的操作都是对形参这个值的修改,不影响实际参数的值。

引用传递:

也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,函数接收的是原始值的内存地址;在方法执行中,形参和实参内容相同,指向同一块内存地址,方法执行中对引用的操作将会影响到实际对象。 demo1:

值传递:

    public static void main(String[] args)

         System.out.println(" 值传递测试 ");

         int a = 10;

         int b = 20;

         System.out.println("before swap " + "a = " + a + " b = " + b);

         swap(a, b);

         System.out.println("after swap " + "a = " + a + " b = " + b);

    

    public static void swap(int a, int b)

         int temp = a;

  1. = b;
  2. = temp;

         System.out.println("swaping " + "a = " + a + " b = " + b);

    

before swap a = 10 b = 20 swaping a = 20 b = 10 after swap a = 10 b = 20

技术分享图片

    从jvm的角度来理解的话,因为main也是一个方法,所以这个时候基本数据类型也是在栈中的,

假如是全局的话就会在堆中。要想理解基本数据类型的值传递我们首先来回顾一下jvm加载数据的过程,

从上面的例子我们看到,首先jvm会把class的相关信息放到方法区中,当代码执行到int a=10和int b=20的时候会把这些信息放到栈中去也就是局部变量表中去。这个时候在栈中的基本数据类型它本身就是真身,现在假如把它传到方法中进行对值进行操作的话,jvm规定就会复制一份数据出来,这个时候操作的就是数据复制的信息。而假如是引用数据类型的话,我们来看下面的案例

public class ParamTest

    public static void main(String[] args)

         Student a = new Student(0);

         Student b = new Student(100);

         System.out.println("交换前:");

         System.out.println("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore());

         swap(a, b);

         System.out.println("交换后:");

         System.out.println("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore());

    

    public static void swap(Student x, Student y)

         x.setScore(99);

         y.setScore(99);

    

交换前:

a的分数:0.0--- b的分数:100.0 交换后:

a的分数:99.0--- b的分数:99.0

技术分享图片

我们再来看看引用数据类型的jvm的加载过程,首先当jvm类加载器加载class文件的时候会把所有的信息都放到方法区中,这个时候当加载到Student a = new Student(0); Student b = new Student(100);的时候会把a,b如栈,这个时候a.b只是引用的而真正的两个 student都在堆中,a和b只不过是化身而已,这个时候我们假如对化身a和b进行操作的话,它a和b本身就是对堆中的引用地址的代表,a和b这个时候就代表student在堆中的内存地址,所以这个时候假如对a和b进行操作的话就是对堆中的对象进行操作,所以会引起值的变化。

理解了上面的值传递和引用传递我们再来看看下面的案例

public static void main(String[] args)

         System.out.println(" 值传递测试 ");

         int a = 10;

         int b = 20;

         System.out.println("before swap " + "a = " + a + " b = " + b);

         swap(a, b);

         System.out.println("after swap " + "a = " + a + " b = " + b);

    

    public static void swap(int a, int b)

  1. = 1;
  2. = 1;

         System.out.println("swaping " + "a = " + a + " b = " + b);

    

before swap a = 10 b = 20 swaping a = 1 b = 1

after swap a = 10 b = 20

道理就是值传递,我们主要是为了引出下面的例子 public static void main(String[] args)

         Student a = new Student(0);

         Student b = new Student(100);

         System.out.println("交换前:");

         System.out.println("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore());

         swap(a, b);

         System.out.println("交换后:");

         System.out.println("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore());

    

    public static void swap(Student x, Student y)

x=new Student(99);

y=new Student(99);

    

交换前:

a的分数:0.0--- b的分数:100.0

交换后:

a的分数:0.0--- b的分数:100.0

技术分享图片

从上面我们可以看到本来a和b都分别指向堆中的new student(0)和student(10)的现在在另一个方法中现在又有x和y指向了堆中的new student(99)和new student(99)所以这个时候a和b的引用地址还是不变的,只不过是xy的变化了而已。本质是a和b分别指向了两个不同的堆内存空间

我们在来看final的例子

    public static void main(String[] args)

// String、Char、Byte、Short、Integer、Long、Float、Double等final修饰的类

         // 对形参修改时实参不受影响

         System.out.println(" final修饰的类-特殊的引用传递测试 ");

         String str = "我是final我不变";          swapByFinalClass(str);

         System.out.println("after swapByFinalClass, str = " + str);

    

    /**

     * final修饰的类做形参时, 修改形参不影响实参

     */

    public static void swapByFinalClass(String str)

         str = "我是形参";

         System.out.println("swapByFinalClassing : str = " + str);

    

final修饰的类-特殊的引用传递测试

swapByFinalClassing : str = 我是形参 after swapByFinalClass, str = 我是final我不变

技术分享图片

只不过这个时候数据放在了方法区的常量池中了,而不是在堆中了。

1)使用基本类型的变量a、b通过swap方法进行的是值传递,对形参修改但实参未改变,

利用内存模型详解原理:

技术分享图片

2)使用类ReferenceObj的实例变量obj,通过swapByReference()进行的是引用传递的方式,具体的内存模型如下:

技术分享图片

通过上面的分析,对于传递方式应该很好理解了^_^

注意:这里要特殊考虑String,以及Integer、Double等基本类型包装类,它们的类前面都有final修饰,为不可变的类对象,每次操作(new或修改值)都是新生成一个对象,对形参的修改时,实参不受影响,与值传递的效果类似,但实际上仍是引用传递。

总结:

  1. 基本类型变量作为方法中的参数,进行的值传递,对形参的修改不影响实参的原来的值;
  2. 非final修饰的类、数组、接口作为方法中的参数,进行的引用传递(地址传递),对形参修改后实参也会改变,因为二者指向的是同一个实例;
  3. final修饰的类作为方法中的参数,因为final的存在初始化后值不可变,每次操作都相当于产生一个新的实例对象,因此对形参修改时,实参也不受影响。

jvm基础--jvm内存模型

jvm基础–JVM内存模型文章目录jvm基础--JVM内存模型1.JVM内存模型1.JVM内存模型参考:深入理解JVM-JVM内存模型面试必问之JVM原理深入理解JVM-内存模型(jmm)和GC 查看详情

jvm基础--jvm内存模型

jvm基础–JVM内存模型文章目录jvm基础–JVM内存模型1.JVM内存模型1.JVM内存模型参考:深入理解JVM-JVM内存模型面试必问之JVM原理深入理解JVM-内存模型(jmm)和GC 查看详情

深入理解jvm——jvm内存模型

JVM内存模型Java虚拟机(JavaVirtualMachine=JVM)的内存空间分为五个部分,分别是: 1.程序计数器 2.Java虚拟机栈 3.本地方法栈 4.堆 5.方法区。下面对这五个区域展开深入的介绍。 1.程序计数器1.1.什么是程序计数器... 查看详情

打怪升级jvm关于jvm内存模型及gc调优(代码片段)

JVM调优,其实就是调整SWT和FGC的过程 JVM内存模型  通过一张基础的图了解最简单的JVM模型:    其实在jvm模型中,主要包含了我们常见的堆栈方法区等待--每个版本不同可能解释有所不同,这里默认以8版本为例:  ... 查看详情

深入理解jvm—jvm内存模型

原文地址:http://www.lofter.com/app/QRCodedownload?act=qbbkdlxz_20150313_13我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内... 查看详情

深入理解jvm—jvm内存模型

http://www.cnblogs.com/dingyingsi/p/3760447.html#3497199我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户I... 查看详情

(转)深入理解jvm—jvm内存模型

原文地址:http://www.cnblogs.com/dingyingsi/p/3760447.html我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲... 查看详情

深入理解jvm—jvm内存模型

我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等待成本,但是随着CPU的发... 查看详情

深入理解jvm—jvm内存模型

我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等待成本,但是随着CPU的发... 查看详情

jvm内存模型理解

Java虚拟机(JavaVirtualMachine简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境,它是Java最具吸引力的特性之一。JVM内存模型 1.方法区和堆是所有线程共享的数据区1)堆:存放对象的实例2)方法区:存放已被虚... 查看详情

jvm内存模型的理解

今天周六,又开始啃一遍《深入理解java虚拟机》每次读的感觉不一样,大学代码量较少,读起来也就死记硬背。1.堆:长度可变,运行时使用的变量;存放对象(new)和数组之类;2.栈:长度可变,在分配内存之前,固定了其大... 查看详情

jvm——内存模型

这是我理解的jvm内存模型,这一整块可以简单理解为虚拟内存空间:jvm代码、jvm数据:是运行jvm所用到的代码和数据,并不是我们自己编程得到的代码和数据directmemory:主要是NIO在使用CodeCache:即时编译器生成的本地代码JNI代码... 查看详情

深入理解jvm内存结构-垃圾回收-类加载&字节码技术-内存模型(代码片段)

资料下载内容参考一、什么是JVM?JVM(JavaVirtualMachine)其实就类似于一台小电脑运行在windows或者linux这些操作系统环境下。它直接和操作系统进行交互,与硬件不直接交互,然后操作系统可以帮我们完成和硬... 查看详情

深入理解jvm内存结构-垃圾回收-类加载&字节码技术-内存模型(代码片段)

资料下载内容参考一、什么是JVM?JVM(JavaVirtualMachine)其实就类似于一台小电脑运行在windows或者linux这些操作系统环境下。它直接和操作系统进行交互,与硬件不直接交互,然后操作系统可以帮我们完成和硬... 查看详情

jvm-内存模型

本篇其实就是一个读书笔记,书是《深入理解JAVA虚拟机》,在网上搜索JAVA内存,说的比较好的其实很多都源自这本书,作为一个JAVA程序员,理解虚拟机是通向高级程序员的必经道路。本篇中的图片源自当时网上,具体出处找不... 查看详情

深入理解jvm

深入理解JVM原文链接:https://www.cnblogs.com/dingyingsi/p/3760447.html整理:CCSoft 虚拟机内存模型中定义的访问操作如下图所示:图1.JVM虚拟机定义的访问操作java中通过多线程机制使得多个任务同时执行处理,所有的线程共享JVM内存... 查看详情

jvm运行时内存划分

(根据《深入理解java虚拟机》这本书总结)   本文主要解释jvm内存模型,以及各个部分的作用。都是自己总结的给自己看的通俗语言,未用专业术语的见谅。  一、为什么要了解jvm内存模型?  在了解一个类的编译-... 查看详情

深入理解jvm内存模型

我们知道,计算机CPU和内存的交互是最频繁的,内存是我们的高速缓存区,用户磁盘和CPU的交互,而CPU运转速度越来越快,磁盘远远跟不上CPU的读写速度,才设计了内存,用户缓冲用户IO等待导致CPU的等待成本,但是随着CPU的发... 查看详情