Java当中的内存分配以及值传递问题内存解析

首先必须说明作为Java程序员对于内存只要有大致的了解就可以了,如果你对Java当中的某一个知识点在不需要分析内存分配过程的情况下可以掌握,那就大可不必去研究内存。如果你对知识点已经掌握,那么你应该把更多的精力放在对业务逻辑的分析与设计上,这样的话你才可能这一行业走的更远。

好了废话不多说了,下面我带着大家先来简单的看一下Java当中所涉及的内存分配,接着我会以讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态。

一、Java当中所涉及到的内存分类

Java当中你知道这5种内存就够用了,下面对这5种内存里面所存放的数据做一解释。

①     栈内存:它里面存放的是引用(也就是地址,Java当中的这个地址并非内存的物理地址,但是它通过这个地址找到它所指向地址的内容)还有就是基本类型的值以及方法的形参也是存放在栈内存当中的。

②     对内存:它里面存放的是对象、引用、基本类型的值(对于引用和基本类型的值什么时候放在栈里什么时候放在堆里,在后面讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态的时候会说)。

③     寄存器:它里面存放的是中间运算的数字(对于这个我们可以忽略不去考虑它)。

④     代码段:顾名思义它里面放的就是程序的代码。

⑤     池内存:池里面方的是常驻内存反复利用的数据。

好了这就是Java当中常见内存以及它里面所存放的数据,下面我们通过讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态。

二、Java当中的值传递问题以及代码执行过程当中内存的状态

什么是值传递?

值传递就是Java当中参数传递的一种方式(而且也是唯一的一种方式,也就是说Java当中只有值传递),所谓参数传递就是在某个方法被调用的时候把一个实参传递给形参的过程。

下面我们通过分析下面代码执行过称中内存的状态来说明Java当中的参数传递以及为什么Java当中只有值传递。

代码清单:(为了节省空间格式不是很规范)

定义学生类:


定义测试类:


测试结果:


为什么会有这样的结果?下面我们分析一下这段代码执行过称当中内存的分配,相信问题将迎刃而解。

1、  我们运行TestPassing这类,虚拟机加载TestPassing这个类,虚拟机将这些代码存放到代码段当中(这里我们就不画出代码段的图示了,后面虚拟机调用任何方法(包括构造方法)都要先到代码段中去找,但是这比较简单也不是重点接下来的解析当中如果涉及到方法调用就不再说明了),然后虚拟机从代码段当中找到main()方法,开始执行代码。

此时虚拟机为main()创建栈内存,内存分配如下

2、  接着执行int age = 20;这行代码,由于它是基本类型的局部变量所以直接把它的值20存在栈内存名字叫age,内存分配如下

3、  接着执行TestPassing tp = new TestPassing();这一行代码,这句话在内存当中做了3个操作,首先TestPassing tp,tp是一个引用类型的变量所以给它分配一块栈内存存放一个TestPassing对象的引用(也就是地址假设这个地址是ox 1a2b3c),接下来在堆内存创建一个TestPassing对象,接着把刚才栈里面tp的引用指向堆里面的这个TestPassing对象,这行代码的顺序之所以是这样是因为“=”的优先级比“new”的优先级低。内存分配如下(在此只给出最终内存分配图)

4、  接着执行这一行代码tp.addAge(age); TestPassing对象调用addAge(int age)方法,虚拟机为addAge(int age)方法分配一个临时的栈内存,并且在这块临时栈内存当中为addAge(int age)的形参age也分配一小块栈内存,接着把main()当中的实参age的副本(注意是实参age的副本而不是实参age)传给形参age,由于实参age是基本类型所以实参age的副本就是20,也就是说把20传给形参age,此时的内存分配如下

这行代码到此还没有执行完,参数传过去之后接着程序跳到被调方法当中去执行,也就是执行age++;此时操作的是形参age与实参age没有任何关系,age++;完了之后形参age的值变成21,此时的内存分配如下

被调方法还没结束,程序接着往下执行到方法体的结束大括号,被调方法执行完毕,同时addAge(int age)的临时栈内存关闭。此时的内存分配如下

5、  程序接着执行System.out.println("age=" + age);(这一行代码的内存分配过程我想没人想让我画吧)这一行代码,很清楚看上面的内存图,也就不难理解为什么打印出20了。

6、  接下来程序执行到Student s1 = new Student();这一行代码和上面TestPassing tp = newTestPassing();内存分配的过称基本一样,这句话也是在内存当中做了3个操作,首先Student s1,s1是一个引用类型的变量所以给它分配一块栈内存存放一个Student对象的引用(假设这个地址是ox1a2b3d),接下来在堆内存创建一个Student对象,它有一个int类型属性age,所以在刚才创建的对象的大块内存当中分出一小块来存放这个属性,里面存的值是0名在叫age(全局变量有默认值所以我们没给它赋值就默认为0),接着把刚才栈里面s1的引用指向堆里面的这个Student对象。此时的内存分配如下

7、  接着程序执行s1.age = 20;这一行代码,这行代码将堆内存当中的Student对象的age改为20,此时的内存分配如下

8、  接着程序执行tp.addAge(s1);这一行代码,TestPassing对象调用addAge(Student s)方法,虚拟机为addAge(Student s)方法分配一个临时的栈内存,并且在这块临时栈内存当中为addAge(Student s)的形参s也分配一小块栈内存,接着把main()当中的实参s1的副本传给形参s,但是s1是引用类型它的副本就是它现在在栈内存里面的地址,也就是说把Student的地址传给形参s,所以形参就会根据这个地址找到Student对象,此时的内存分配如下

这行代码到此还没有执行完,参数传过去之后接着程序跳到被调方法当中去执行,也就是执行s.age++;此时它操作的时是真正的Student对象,所以这行代码执行完了之后Student对象的age属性就变成了21,此时的内存分配如下

被调方法还没结束,程序接着往下执行到方法体的结束大括号,被调方法执行完毕,同时addAge(Student s)的临时栈内存关闭。此时的内存分配如下

9、  程序接着执行System.out.println("s1.age=" + s1.age);这一行代码,很清楚看上面的内存图,也就不难理解为什么打印出21了。

10、  main()结束,main()栈内存关闭,没有任何引用指向堆内存当中的TestPassing对象和Student对象,垃圾回收器回收资源,虚拟机关闭。

好了关于Java当中的内存分配以及值传递问题内存解析就说到这,Java当中的池内存也是一个很重要的概念,由于时间关系本次分析并未提及池内存,有时间再给大家分享。可以给大家一个思考题,如果给addAge(int age) 这个方法再加一String类型的形参也就是把这个方法改成addAge(int age, String name)并在这个方法里面改变name的值,给Student类再加一个属性String name,并在addAge(Student s)方法当中修改s.name的值,这样的话String是引用类型,那么name会怎样变呢?

时间: 2024-09-30 19:02:09

Java当中的内存分配以及值传递问题内存解析的相关文章

java内存分配和String类型的深度解析(转)

一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析 关于String的许多令人迷惑的问题.下面是本文将要涉及到的一些问题,如果读者对这些问题都了如指掌,则可忽略此文. 1.java内存具体指哪块内存?这块内存区域为什么要进行划分?是如何划分的?划分之后每块区域的作用是什么?如何设置各个区域的大小? 2.String类型在执行连接操作时,效率为什么会比StringBuffer或者StringBu

【转】java内存分配和String类型的深度解析

一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析关于String的许多令人迷惑的问题.下面是本文将要涉及到的一些问题,如果读者对这些问题都了如指掌,则可忽略此文. 1.java内存具体指哪块内存?这块内存区域为什么要进行划分?是如何划分的?划分之后每块区域的作用是什么?如何设置各个区域的大小? 2.String类型在执行连接操作时,效率为什么会比StringBuffer或者StringBui

【Java】基本类型和引用类型(值传递)

[关键词] [问题] · 加深对基本类型和引用类型的理解: [效果图] [分析] 參见最后的[參考资料] [解决方式] [代码] public void test() throws Exception { System.out.println("\nint:================="); int i = 2; System.out.println("before:" + i); change1(i); System.out.println("af

Java内存分配和String类型的深度解析

一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析关于String的许多令人迷惑的问题.下面是本文将要涉及到的一些问题,如果读者对这些问题都了如指掌,则可忽略此文. 1.java内存具体指哪块内存?这块内存区域为什么要进行划分?是如何划分的?划分之后每块区域的作用是什么?如何设置各个区域的大小? 2.String类型在执行连接操作时,效率为什么会比StringBuffer或者StringBui

java之方法的参数传递(值传递和引用传递)

方法,必须有其所在类或对象调用时才有意义,若方法有参数: 形参:方法声明时的参数: 实参:方法调用时实际传给形参的参数值: java的实参如何传入方法呢? 首先要明确:变量分为两大类:基础数据类型.引用数据类型. 基础数据类型参数传递方式只有一种:值传递.即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响: public class Test{ public static void test(int i) { i = 6; System.out.println(i); } public

JVM内存划分以及值传递和引用传递的区别

4-8-2017_SHJavaTraing_Day05 一.JVM对自己的内存划分为5个区域    1.方法栈:所有的方法运行的时候进入内存    2.堆:存储的是容器和对象    3.方法和数据共享: 运行时期class文件进入的地方    4.本地方法栈: JVM调用了系统中的功能    5.寄存器:内存和CUP之间 二.值传递和引用传递的区别(易错内容) 1.方法参数是基本数据类型 方法参数是基本数据类型时,传递的是值. 1 //演示方法参数是基本数据类型的传递 2 class Demo{

memcached学习——memcached的内存分配机制Slab Allocation、内存使用机制LRU、常用监控记录(四)

内存分配机制Slab Allocation 本文参考博客:https://my.oschina.net/bieber/blog/505458 Memcached的内存分配是以slabs为单位的,会根据初始chunk大小.增长因子.存储数据的大小实际划分出多个不同的slabs class,slab class中包含若干个等大小的trunk和一个固定48byte的item信息.trunk是按页存储的,每一页成为一个page(默认1M). 1.slabs.slab class.page三者关系: sl

动态内存分配存在的问题(内存空洞)------c++程序设计原理与实践(进阶篇)

new的问题究竟在哪里呢?实际上问题出在new和delete的结合使用上.考察下面程序中内存分配和释放过程: while(1){ Big* p=new big; //...... Small* n1=new Small; //...... delete p; Small* n2=new Small; //...... } 在每个循环步中,我们创建了两个Small,在此期间,我们还分配了一个Big,然后又释放了它.考察这段代码,每执行一个循环步,我们可能期望"消耗" 2*sizeof(S

Java内存分配、管理小结

 想写这篇总结酝酿了有个来月了,却始终感觉还差点什么东西,一直未敢动笔. 最近两天连夜奋战,重新整理下前面查阅的资料.笔记,还是决定将它写出来. 现在提出几个问题,如果都能熟练回答的大虾,请您飘过.如以往一样,我是小菜,本文自然也是针对小菜阶层的总结. 首先是概念层面的几个问题: Java中运行时内存结构有哪几种? Java中为什么要设计堆栈分离? Java多线程中是如何实现数据共享的? Java反射的基础是什么? 然后是运用层面: 引用类型变量和对象的区别? 什么情况下用局部变量,什么情况下用