菜鸟学Java(二十三)——Java内存分析

我们常说的Java内存主要分为四大块(寄存器不在考虑之内,我们无法用代码来操控它):stack(栈)、heap(堆)、data segment(数据区)、code segment(代码区)。它们的主要用途如下图所示:

而在上面四个当中,我们经常谈论的是右边那两个家伙——stack和heap。今天我们就来聊聊Java代码在运行的过程中,在stack和heap中到底是什么样子的吧。

我们先看下面一段代码:

	public static void main(String[] args) {
		TestReference testReference = new TestReference();
		int age = 1;
		Person xiaoqiang = new Person("小强", 21);
		Person xiaoming = new Person("小明", 22);

		testReference.selfPlus(age);
		System.out.println("age经过selfPlus方法的处理后为:" + age);

		testReference.changeName(xiaoqiang);
		System.out.println("小强经过changeName方法的处理后的名字为:" + xiaoqiang.getName());

		testReference.changeAge(xiaoming);
		System.out.println("小明经过changeAge方法的处理后的年龄为:" + xiaoming.getAge());

	}

	public void selfPlus(int i) {
		i = i + 1;
	}

	public void changeName(Person person) {
		person = new Person("小刚");
	}

	public void changeAge(Person person) {
		person.setAge(25);
	}

执行完以上代码,会打印出什么内容呢?如果你Java基础还可以,那么很容易就能知道会输出什么内容,想要知道以上代码会打印什么内容,需要你明白Java代码在stack和heap中是怎么工作的。下面我们结合几张图来看看:

		int i = 1;
		Person xiaoqiang = new Person("小强", 21);
		Person xiaoming = new Person("小明", 22);

当我们执行完上面三行代码时,内存中的情况如下图所示:

我们知道stack是用来存放变量的,所以age、xiaoqiang和xiaoming三个变量会被存放到stack里,而age又是int类型,所以它的值也会被存放在stack里面。xiaoqiang和xiaoming为Person类型的引用变量,所以stack只会存放它们的一个引用,也就是对应对象的内存地址,而它们真正的内容被存放在了heap里面。

当执行到testReference.selfPlus(i);时,selfPlus(int i)方法被调用,此时会在stack中为形参I开辟一块内存空间,并将其值设置为1,此时内存中的情况如下:

因为调用selfPlus(int i)方法时,将age作为形参传递给该方法,相当于将age的值复制一份给i,所以现在i的值为1,接着执行i = i + 1;此时i的值被修改为2,如图:

此时被修改的只是age的副本,而并非age本身,所以age仍为1,当selfPlus(int i)方法被执行完,i被销毁,age不变。接下来看xiaoqiang,当调用changeName(Person person)方法时,会在stack中为person分配一块空间,里面存放的是xiaoqiang指向的地址,如图:

然后执行到person = new Person("小刚");时,由于又new了一个Person对象,所以在堆中会新建一个person,姓名叫小刚,并且会将person执行小刚的地址,如图:

此时,person指向的对象由小强变成了小刚,但是xiaoqiang所指向的对象仍然是小强,并没有发生任何变化。当changeName方法执行完以后,person被销毁,而小刚也会因为没有任何对象对其进行引用,随后被垃圾回收器回收掉。

下面到了最后一个方法了,当执行testReference.changeAge(xiaoming);时,调用changeAge(Person person)方法,同样会在stack中为person分配一块空间,这次里面存放的是xiaoming对应的内存地址,如图:

然后执行person.setAge(25);,此时person指向的对象为小明,所以在执行setAge方法时,修改的就是heap中小明的属性值,此时小明的年龄被修改为25,。方法执行完毕,person被销毁,内存中最终结果如下:

内存中的最终情况就如上图所示,当然当main方法执行完以后,之前创建的所有对象都会被销毁。OK,每天上班做项目的你是不是把一些基础的东西忘了呢?如果是的话就赶快来补一补吧,好处还是很多的。

时间: 2024-08-28 21:26:32

菜鸟学Java(二十三)——Java内存分析的相关文章

Eclipse中的快捷键快速生成常用代码(例如无参、带参构造,set、get方法),以及Java中重要的内存分析(栈、堆、方法区、常量池)

Eclipse中的快捷键快速生成常用代码(例如无参.带参构造,set.get方法),以及Java中重要的内存分析(栈.堆.方法区.常量池) 以上就是Eclipse中的快捷键快速生成常用代码(例如无参.带参构造,set.get方法),以及Java中重要的内存分析(栈.堆.方法区.常量池)的全部内容了,更多内容请关注:CPP学习网_CPP大学 本文固定链接:CPP学习网_CPP大学-Eclipse中的快捷键快速生成常用代码(例如无参.带参构造,set.get方法),以及Java中重要的内存分析(栈.

java基础知识回顾之---java String final类 容易混淆的java String常量池内存分析

/** *   栈(Stack) :存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放  在常量池中). 堆(heap):存放所有new出来的对象. *   静态存储:存放静态成员(static定义的). 常量池(constant pool):在堆中分配出来的一块存储区域,存放储显式的String常量和基本类型常量(float.int等).另外,可以存储不经常改变的东西 *                       p

菜鸟学SSH(十三)——Spring容器解析及简单实现

最近一段时间,"容器"两个字一直萦绕在我的耳边,甚至是吃饭.睡觉的时候都在我脑子里蹦来蹦去的.随着这些天一次次的交流.讨论,对于容器的理解也逐渐加深.理论上的东西终归要落实到实践,今天就借助Spring容器实现原理,简单说说吧. 简单的说,Spring就是通过工厂+反射将我们的bean放到它的容器中的,当我们想用某个bean的时候,只需要调用getBean("beanID")方法. 原理简单介绍: Spring容器的原理,其实就是通过解析xml文件,或取到用户配置的

4.3-全栈Java笔记:面向对象的内存分析

面向对象的内存分析 为了让大家对于面向对象编程有更深入的了解,我们要对程序的执行过程中,内存到底发生了什么变化,进行剖析,让大家做到"心中有数",通过更加形象的方式理解程序的执行方式. 老鸟建议:       本节是为了让初学者更深入了解程序底层执行情况,为了完整的体现内存分析流程,会有些新的名词,比如:线程.Class对象.大家暂时可以不求甚解的了解,后期学了这两个概念再回头来看我们这篇内存分析,肯定收获会更大. Java虚拟机的内存可以分为三个区域:栈stack.堆heap.方法区

Java面向对象_对象内存分析—值传递和引用传递

对象内存分析,我一直学的比较模糊,今天抽空整理一下,希望能理清. 先说一下,内存分析从何而来,这对于我们这些刚接触java的人来说都比较模糊,就从new关键字说起吧. new关键字表示创建一个对象或者说是实例化对象或者说是申请内存空间,所有程序运行都需要内存来存储数据,这样内存的概念就出来了.举个例子说明:定义一个类Person,属性:char a;int sge;那么创建一个对象申请的内存空间就是所有属性所占字节之和,为6个字节. 详细理一下对象在内存中的结构: Person p=new Pe

Java之十三 Java多线程

你一定知道多任务处理,因为它实际上被所有的现代操作系统所支持.然而,多任务处理有两种截然不同的类型:基于进程的和基于线程的.认识两者的不同是十分重要的.对很多读者,基于进程的多任务处理是更熟悉的形式.进程(process)本质上是一个执行的程序.因此,基于进程(process-based)的多任务处理的特点是允许你的计算机同时运行两个或更多的程序.举例来说,基于进程的多任务处理使你在运用文本编辑器的时候可以同时运行Java编译器.在基于进程的多任务处理中,程序是调度程序所分派的最小代码单位.在基

Java基础学习笔记二十三 Java核心语法之反射

类加载器 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,链接,初始化三步来实现对这个类进行初始化. 加载就是指将class文件读入内存,并为之创建一个Class对象.任何类被使用时系统都会建立一个Class对象. 链接指的是将Java类的二进制代码合并到JVM的运行状态之中的过程.在链接之前,这个类必须被成功加载.类的链接包括验证.准备和解析等几个步骤. 验证:是否有正确的内部结构,并和其他类协调一致. 准备:负责为类的静态成员分配内存,并设置默认初始化值 解析:

java 构造方法执行过程内存分析

package Demo; public class BirthDate { private int day; private int month; private int year; public BirthDate(int d, int m, int y) { day = d; month = m; year = y; } } java bean package Demo; public class Demo { public static void main(String[] args)

Java学习系列(二十三)Java面向对象之内部类详解

一.前言 内部类也称寄生类,就是把一个类放在类里面(即内部类的上一级程序单元是类)定义,将其作为外部类的成员.内部类主要用几种定义形式:静态(static)内部类,非静态内部类,匿名内部类(也就是没有名字的寄生类).内部类的好处就是内部类可以直接外部类的(包括私有)成员,反之不能.下面我们通过一些实例来详细讲解一下Java中内部类的使用及几种定义形式的相互调用. 二.实例说明 (1)匿名内部类:当程序创建匿名内部类时,会立即创建匿名内部类(实现类)的实例. interface IBreathe

Java二十三设计模式之------享元模式

一.享元模式(Flyweight) 享元模式的主要目的是实现对象的共享,即共享池,当系统中对象多的时候可以减少内存的开销,通常与工厂模式一起使用. FlyWeightFactory负责创建和管理享元单元,当一个客户端请求时,工厂需要检查当前对象池中是否有符合条件的对象,如果有,就返回已经存在的对象,如果没有,则创建一个新对象,FlyWeight是超类.一提到共享池,我们很容易联想到Java里面的JDBC连接池,想想每个连接的特点,我们不难总结出:适用于作共享的一些个对象,他们有一些共有的属性,就