首先必须说明作为Java程序员对于内存只要有大致的了解就可以了,如果你对Java当中的某一个知识点在不需要分析内存分配过程的情况下可以掌握,那就大可不必去研究内存。如果你对知识点已经掌握,那么你应该把更多的精力放在对业务逻辑的分析与设计上,这样的话你才可能这一行业走的更远。
好了废话不多说了,下面我带着大家先来简单的看一下Java当中所涉及的内存分配,接着我会以讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态。
一、Java当中所涉及到的内存分类
Java当中你知道这5种内存就够用了,下面对这5种内存里面所存放的数据做一解释。
① 栈内存:它里面存放的是引用(也就是地址,Java当中的这个地址并非内存的物理地址,但是它通过这个地址找到它所指向地址的内容)还有就是基本类型的值以及方法的形参也是存放在栈内存当中的。
② 对内存:它里面存放的是对象、引用、基本类型的值(对于引用和基本类型的值什么时候放在栈里什么时候放在堆里,在后面讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态的时候会说)。
③ 寄存器:它里面存放的是中间运算的数字(对于这个我们可以忽略不去考虑它)。
④ 代码段:顾名思义它里面放的就是程序的代码。
⑤ 池内存:池里面方的是常驻内存反复利用的数据。
好了这就是Java当中常见内存以及它里面所存放的数据,下面我们通过讲解Java当中的值传递问题,分析在代码执行的过程当中内存的状态。
二、Java当中的值传递问题以及代码执行过程当中内存的状态
什么是值传递?
值传递就是Java当中参数传递的一种方式(而且也是唯一的一种方式,也就是说Java当中只有值传递),所谓参数传递就是在某个方法被调用的时候把一个实参传递给形参的过程。
下面我们通过分析下面代码执行过称中内存的状态来说明Java当中的参数传递以及为什么Java当中只有值传递。
代码清单:(为了节省空间格式不是很规范)
定义学生类:
定义测试类:
测试结果:
为什么会有这样的结果?下面我们分析一下这段代码执行过称当中内存的分配,相信问题将迎刃而解。
1、 我们运行TestPassing这类,虚拟机加载TestPassing这个类,虚拟机将这些代码存放到代码段当中(这里我们就不画出代码段的图示了,后面虚拟机调用任何方法(包括构造方法)都要先到代码段中去找,但是这比较简单也不是重点接下来的解析当中如果涉及到方法调用就不再说明了),然后虚拟机从代码段当中找到main()方法,开始执行代码。
此时虚拟机为main()创建栈内存,内存分配如下
存在栈内存名字叫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会怎样变呢?
Java当中的内存分配以及值传递问题内存解析的更多相关文章
-
Java方法的参数传递方式为: 值传递
Java方法的参数传递方式为: 值传递 对于基本数据类型作为参数传递时, 是"按值传递", 这点都认识很清楚. 但是, 当对象或者说引用作为参数传递, Java 的参数传递方式是& ...
-
Java内存分配及值、引用的传递
关于堆栈的内容网上已经有很多资料了,这是我找的加上自己理解的一篇说明文: 一.内存区域类型 1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制: 2. 栈:存放基本类型的变量数 ...
-
java内存分配和String类型的深度解析
[尊重原创文章出自:http://my.oschina.net/xiaohui249/blog/170013] 摘要 从整体上介绍java内存的概念.构成以及分配机制,在此基础上深度解析java中的S ...
-
【转】java内存分配和String类型的深度解析
一.引题 在java语言的所有数据类型中,String类型是比较特殊的一种类型,同时也是面试的时候经常被问到的一个知识点,本文结合java内存分配深度分析关于String的许多令人迷惑的问题.下面是本 ...
-
【Java】基本类型和引用类型(值传递)
[关键词] [问题] · 加深对基本类型和引用类型的理解: [效果图] [分析] 參见最后的[參考资料] [解决方式] [代码] public void test() throws Exception ...
-
JAVA方法中参数到底是值传递还是引用传递
当一个对象被当作参数传递到一个方法后,在此方法内可以改变这个对象的属性,那么这里到底是值传递还是引用传递? 答:是值传递.Java 语言的参数传递只有值传递.当一个实例对象作为参数被传递到方法中时,参 ...
-
JAVA 垃圾收集算法,垃圾收集器与内存分配策略(内容全面,解析简单易懂)
垃圾收集器需要解决的三个问题: 1)哪些内存需要回收 2)什么时候回收 3)如何回收 背景:程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭,在这几个区域内不需要过多的考虑回收的问题,因 ...
-
java之方法的参数传递(值传递和引用传递)
方法,必须有其所在类或对象调用时才有意义,若方法有参数: 形参:方法声明时的参数: 实参:方法调用时实际传给形参的参数值: java的实参如何传入方法呢? 首先要明确:变量分为两大类:基础数据类型.引 ...
-
java基础 - 形参和实参,值传递和引用传递
形参和实参 形参:就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的. 形参只有在方法被调用的时候,虚拟机才会分配内存单元,在方法调用结束之后便会释放所分配的内存单元. 因此,形 ...
随机推荐
-
Android获取屏幕实际高度跟显示高度,判断Android设备是否拥有虚拟功能键
//获取屏幕尺寸,不包括虚拟功能高度 getWindowManager().getDefaultDisplay().getHeight(); 获取屏幕原始尺寸高度,包括虚拟功能键高度, private ...
-
同时打开两个excel工作窗口
先打开你想要同时打开的两个excel文件,有两个方法可以同时打开两个窗口:首先选取任意一个文件,1. 点击“窗口”菜单==>"重排窗口"==>选择你想同时打开的样式== ...
-
node.js处理post请求
1.html 2.app.js var http = require('http') var qs = require('querystring') /** * 路由控制的功能 * @param pa ...
-
创建INnodb的compress表
需要将innodb_file_per_table=1 ,innodb_file_format=Barracuda;; 如: Creating a Compressed Table in a Gener ...
-
图片代替radio
<html> <head> <meta http-equiv="Content-Type" content="text/html; char ...
-
CSS中 opacity的设置影响了index(层数)的改变
在使用 opacity 属性来实现页面整体透明的时候,发现了一个问题.如果两个层发生了重叠,使用了 opacity 属性并且属性值小于1的层,会覆盖掉后面的层.于是动手做了个实验,来验证 opacit ...
-
sendrose【SPFA】
之前看到一题需要并查集+SPFA,然后就特别囧的发现自己SPFA这个历史遗留问题已经不知道怎么打了╮(╯▽╰)╭ 就果断挑了一题特别裸的SPFA赶紧搞搞掉,顺便自己乱YY下学SPFA的笔记,免得自己下 ...
-
[转]Decrypt Any iOS Firmware on Mac, Windows, Linux
source:http://www.ifans.com/forums/threads/decrypt-any-ios-firmware-on-mac-windows-linux.354206/ Dec ...
-
Python数据分析之路(一)查询和统计
0. 如何入门数据分析 关注沙漠之鹰的同学一定看过沙漠君写得很多篇数据分析文章,比如分析房价,车价,预测机动车摇号这些话题.其实文章中所有的分析都使用了Python和它非常强大的数据分析库Pandas ...
-
Servlet中Response对象应用1(输出简单文字、实现文件下载)
通过response对象可以实现很多功能,下面的代码都是在myeclipse上实现过的,整理下路,以备后用. response对象应用1 1向客户端发送简单消息 A利用已经声明的对象,调用其getOu ...