Java当中的内存分配以及值传递问题内存解析
首先必须说明作为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基础 - 形参和实参,值传递和引用传递
形参和实参 形参:就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的. 形参只有在方法被调用的时候,虚拟机才会分配内存单元,在方法调用结束之后便会释放所分配的内存单元. 因此,形 ...
随机推荐
- glibc resolv/res_send.c getaddrinfo() buffer stack smash when dealing malformation big DNS Response Package
catalogue . 漏洞简述 . 调试环境搭建 . 漏洞利用 . 漏洞分析 . 缓解修复方案 1. 漏洞简述 0x1: 函数调用顺序 getaddrinfo (getaddrinfo.c) -&g ...
- Scala特性: 隐式转换
1.隐式转换特征: 1)隐式参数的用法 · 获取可能的预期类型 · 获取预期类型,并且拥有预期类型的行为 · 对信息进行补充说明(一般用函数做隐式参数的比较多) 2)隐式类: 3)隐式method:
- python统计nginx脚本信息
#!/usr/bin/env python # -*- coding: utf-8 -*- import urllib2 import json import subprocess import th ...
- [JavaEE]如何唤醒Sleep中的线程
主线程调用子线程的interrupt()方法,导致子线程抛出InterruptedException, 在子线程中catch这个Exception,不做任何事即可从Sleep状态唤醒线程,继续执行. ...
- delete表1条件是另一个表中的数据,多表连接删除(转)
DELETE删除多表数据,怎样才能同时删除多个关联表的数据呢?这里做了深入的解释: 1. delete from t1 where 条件 2.delete t1 from t1 where 条件 3. ...
- Linux Shell 从入门到删除根目录跑路指南
1.变量为空导致误删文件base_path=/usr/sbintmp_file=`cmd_invalid`# rm -rf $base_path/$tmp_file这种情况下如果 cmd 执行出错或者 ...
- 《JavaScript权威指南》学习笔记 第七天 DOM操作
由衷的觉得,随着IT技术的广泛的运用,个人电脑以及智能手机的使用,信息的获取与传播更为简单.但是我们获取有用信息的难度相反是越来越大了,想要保持住自己的注意力越来越难了.除了吃饭睡觉,我的精力都在电脑 ...
- Mysql学习笔记(八)由触发器回顾外键约束中的级联选项
近些天都没有写博客.在学习mysql的知识,通过学习和练习,也熟悉了mysql的函数.触发器.视图和存储过程.并且在实际的开发过程中也应用了一小部分.效果还是十分理想的. 今天晚上在学习触发器模仿in ...
- CSS3系列四(Media Queries移动设备样式)
viewport设置适应移动设备屏幕大小 viewport:允许开发者创建一个虚拟窗口并自定义其窗口的大小或缩放功能 <meta name="viewport" conten ...
- centos忘记root密码,重新设置的方法
今天重新装了一个centos6.6,好像root密码没有叫我设置吧,然后用虚拟机开始安装之前的密码登录显示失败,所以有了下面的彩蛋.....Helloween... 在虚拟机安装了Centos,今天要 ...