【J2SE快速进阶】——Java内存分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/details/42179387

程序的执行过程

        要在Java中分析内存,我们先来了解一下程序的执行过程:

         正如上图所示,大致分为3个步骤:

         1、最开始,我们的程序是存在于硬盘中的,当启动运行时,程序会被加载(load)到内存中去,这里的内存可以看做我们的内存条;

         2、此时,内存中除了存在刚加载的程序的代码,还存在操作系统本身的代码(好吧,此句可以当做废话→_→),操作系统会找到程序中的Main方法开始执行程序;

         3、第三步就是本文的重点,系统在程序执行过程中对内存的管理。在Java中,内存大致会被分为四块——heap(栈)、stack(堆)、data segment(数据段)、code segment(代码段),分别用来存放程序中的局部变量、new出来的对象或数组等、静态变量、程序代码。

         这里主要讨论heap(栈)和stack(堆)。

Java的数据类型

         先来复习一下Java的数据类型,Java中数据类型分为两种,基本数据类型和引用数据类型,如下图:

        基本数据类型就这八种,引用数据类型包括类、数组、接口等。

      (初学者可能会把String、Integer等类型和char、int等基本数据类型混淆,这里说明一下,Integer相当于int的“包装类”,String可以看做是char[]类型的数组,此外,Byte、Float等类似。所以这些类型应当当做引用类型去对待。)

内存分析

       正如第一张图所示,程序运行时,我们定义的局部变量一般都存放于栈内存中,这些局部变量既可以是基本数据类型的变量(基本数据类型的变量在栈中直接保存它的值),也可以是引用类型的变量(引用类型的变量在栈中保存的是它所指向的堆内存中对象的地址)。

       堆内存中存放的就是引用类型变量的地址所指向的对象。        

       实践出真知,下面具体在代码中分析一下,希望您能抽出10秒钟来仔细看一下这段代码再继续:

public class MemoryAnalysis {
	public static void main(String[] args) {				
        Person person=new Person("小明",15);
		String newName="小红";
		int newAge=18;
		person.SetName(newName);
		person.SetAge(newAge);
		person.SayHello();
	}
}
public class Person {
	public String name;
	public int age;
	public Person(String name,int age){
		this.name =name;
		this.age=age;
	}
	public void SetName(String name){
		this.name=name;
	}
	public void SetAge(int age){
		this.age=age;
	}
	public void SayHello(){
		System.out.println("我的名字叫"+name+",我"+age+"岁了");
	}
}

       下面对Main方法中的代码逐句分析  :

Person person=new Person("小明",15);

       实例化了Person类后:

       堆内存:堆内存中分配一块内存用来存放Person实例person中的数据,因为person的name属性为String类型,所以在堆内存中会另外分配一块内存用来存放String类型的“小明”,person中的name中存放的只是一个地址(地址3),这个地址指向存放“小明”的内存块;person中age属性为int类型,所以直接在age的内存单元存放int类型的“15”。

        栈内存:因为person为引用类型,所以在栈内存中分配的内存单元person中存放的是一个指向堆内存中person实例的地址(地址1)。如果上面的代码中只是定义了person,而没有new,那么只会在栈内存中分配一个person的内存单元,内才能中的值为空。

String newName="小红";

       因为newName类型为String类型,所以newName的实际内容也会存放于堆内存中,栈内存分配的内存单元newName中只是存放指向堆内存中“小红”的地址。

int newAge=18;

      newAge的类型为int类型,所以直接将值(18)存放在栈内存分配的单元newAge中。

person.SetName(newName);

       到了函数这块,会有一点点复杂,因为SetName(String name)函数有一个类型为引用类型参数name,而且传入的实参为newName,这时newName中存储的值(地址2)会赋值给这个name,所以这时newName和name存储的地址相同(即同时指向“小红”)。同理,当执行了SetName(String name)函数中的this.name=name时,会把栈内存中name中存储的值(地址2)赋值给堆内存中person的name,此时person中的name里存储的也是指向“小红”的地址。

        之后,person原来的name值“小明”会在某个时刻被java的垃圾回收机制所回收。

        方法执行完毕后,栈内存中变量name所占的内存被回收。

person.SetAge(newAge);

          跟SetName(String name)一样,执行时也会在栈内存中为形参age分配内存单元,只不过SetAge(int age)函数中的形参age为int类型,所以直接在栈内存分配的单元中直接存储实参的值(18)即可。

方法执行完毕后,栈内存中变量age所占的内存被回收。

         以上就是这几句代码的执行过程。多少懂一些java基础的您肯定已经猜出运行结果了:

         运行结果:            

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏每日一篇技术文章

Swift3.0 - 流控制

需求二: 输入一个顶点 判断是否在X轴上,或者Y轴上,或者既不在x轴,也不再Y轴上

9120
来自专栏梦魇小栈

重新介绍 JavaScript(JS全面系列教程)

为什么会有这一篇“重新介绍”呢?因为 JavaScript 堪称世界上被人误解最深的编程语言。虽然常被嘲为“玩具语言”,但在它看似简洁的外衣下,还隐藏着强大的语...

23320
来自专栏鸿的学习笔记

python稍难一点的说明

8320
来自专栏闪电gogogo的专栏

Python入门学习(一)

看完了莫烦Python的视频,对于Python有了一点感觉,接下来打算把小甲鱼的视频啃完,附上学习网址:http://blog.fishc.com/catego...

54780
来自专栏移动端开发

Swift 面向对象解析(二)

 接着上面一篇说的内容: 一 继承:      苹果继承与水果,苹果是水果的子类,则苹果是一种特殊的水果;这就是继承的关系,这个我们学OC的时候相信也都理解了...

24870
来自专栏我和我大前端的故事

初探 TypeScript函数基本类型泛型接口类内置对象

前段时间有朋友和我推荐 TypeScript ,他说写起来特别爽,让我去试一试,那时候我还在那是啥高深莫测的东西。刚好那段时间忙,一直没有时间看。最近也很忙,还...

78020
来自专栏Android机动车

Java 基础(三)——集合源码解析 Collection

前面我们讲到了集合的定义以及集合的 Iterator。我们知道集合分为 Collection和 Map,今天我们的重点是学习 Collection。

11950
来自专栏一“技”之长

Swift讲解专题十——类与结构体 原

        Swift中的类与结构体十分相似,和Objective-C不同的是,Swift中的结构体不仅可以定义属性,也可以像类一样为其定义方法。

10120
来自专栏Java与Android技术栈

Scala学习笔记(二)

目前,Scala 在国外比较火,Twitter 已经将自己全部的代码从 Ruby 转到了Scala。而且还有 Spark、Kafka、akka 这样的开源项目及...

11230
来自专栏C/C++基础

C++接口继承与实现继承的区别和选择

《Effective C++》条款三十四:区分接口继承和实现继承中介绍的比较啰嗦,概括地说需要理解三点: (1)纯虚函数只提供接口继承,但可以被实现; ...

9010

扫码关注云+社区

领取腾讯云代金券