Java 内存分析(程序实例),学会分析内存,走遍天下都不怕!!!
2021/6/29 7:22:40
本文主要是介绍Java 内存分析(程序实例),学会分析内存,走遍天下都不怕!!!,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
相信大多数的java初学者都会有这种经历:碰到一段代码的时候,不知该从何下手分析,不知道这段代码到底是怎么运行最后得到结果的..... 等等吧,很多让人头疼的问题,作为一名合格的程序员呢,遇到问题一定要思路清晰,不要将错就错,蒙混过关,这一点很重要!
鉴于笔者最近恶补了java基础,在这儿给大家总结了一些java代码内存分析的经验,希望可以对家有所帮助。
在分析内存之前呢,通过这个图让大家明白计算机是怎么处理一个程序的。
简单总结一下:1.计算机首先从硬盘拿到程序,加载(load)到内存区
2.操作系统找到main方法开始执行
3.执行代码过程中的内存管理:
内存中分四块:分别是heap(堆)、stack(栈)、data segment
(数据区)、code segmen(代码区),各个区所存储的内容图中已
标注。
接下来,
给大家举几个例子程序,分别进行内存分析
**
程序一:
**
public class Person { static int id; static int age; Person(int _id, int _age) { id = _id; age = _age; } public static void main(String[] args) { Person tom = new Person(1,25); System.out.println("id= " + id + " age= " + age); } }
内存分析图:
第一步:从main方法入手,首先看到要实现一个Person类对象,该对象的名字是tom,则在stack中立马分配出一块空间用来存tom这个对象名,它指向heap中的Person对象(上文提到heap中存储new出来的东西)
注:图中局部变量tom的内容为xxx,xxx实际为Person对象在堆中的地址,在此处用xxx
代替。
第二步:调用Person中的构造方法,此时定义了两个局部变量(要存储到stack中)_id和_age,则立马在stack中分配两块空间用于存储这两个局部变量,接下来把1和25分别传给这两个变量
第三步:执行id=_id; age=_age;这两句,把_id和_age的值传给Person对象
第四步:局部变量_id和_age消失(java的垃圾回收机制)
到此完成Person对象的创建,执行输出语句,程序运行结束。
**
程序二:super引用,动态绑定及多态
**
说到多态,就先给大家巩固一下动态绑定和多态的概念吧:
动态绑定:在执行期间(非编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
简单的来说,动态绑定就是根据实际new的对象所属的类调用方法,帮助程序的扩展性达到极致。
多态的话,要知道多态产生的三个条件:
1.有继承
2.有重写
3.父类引用指向子类对象
//abstract 关键字 ---> 抽象类,抽象类一定被继承,抽象方法一定被重写 class Animal { //可以这样声明 absrtact class Animal private String name; Animal(String name) { this.name = name; } public void enjoy() { System.out.println("叫声..."); } } class Cat extends Animal{ private String eyescolor; Cat(String n,String c) { super(n); eyescolor = c; } public void enjoy() { System.out.println("猫叫声...."); } } class Dog extends Animal { private String furCorlor; Dog(String n, String f) { super(n); furCorlor = f; } public void enjoy() { System.out.println("狗叫声..."); } } class Lady { private String name; private Animal pet; Lady(String name, Animal pet) { this.name = name; this.pet = pet; } public void petEnjoy() { pet.enjoy(); } } public class TestAnimal { public static void main(String[] args) { Cat c = new Cat("catname","blue"); Dog d = new Dog("dogname","black"); c.enjoy(); d.enjoy(); Lady l1 = new Lady("l1",c); Lady l2 = new Lady("l2",d); l1.petEnjoy(); l2.petEnjoy(); } }
内存分析图:
老样子,从main方法入手:
第一步,Cat c = new Cat("catname","blue");
当你new一个子类对象出来的时候,其内部就已经包含父类对象,并且该子类对象的super引用会指向其父类对象。
此处分两小步给大家说明:(stack中局部变量的产生过程不再赘述,可参考程序一)
(1)new出来的Cat对象中包含Animal对象,Animal对象有一个成员变量name,自动初始化为null,C对象有一个成员变量eyescolor,自动初始化为null。
(2)传“catname”和“blue”这两个参数,“blue”直接赋值给eyescolor,而“catname”传进去后,通过super(n)方法,调用其父类对象的构造方法,使name值等于“catname”
第二步,Dog d = new Dog("dogname","black");
内存分析同第一步(此处省略)
第三步,c.enjoy(); d.enjoy();
此时,c指向的是Cat对象,但是Cat对象中包含其父类Animal对象,这两个对象都含有enjoy()方法,那么应该调用哪一个呢?这就涉及到了java的动态绑定机制了,你new出来的对象实际是Cat对象,那么就调用Cat对象的方法,而不是调用Animal对象的enjoy()方法;同理,d.enjoy()调用的是Dog对象的enjoy()方法。
所以,输出结果为
第四步,Lady l1 = new Lady("l1",c);
Lady l2 = new Lady("l2",d);
内存分析图:
此处Animl的引用pet指向了其子类对象c,也就是上文提到的“父类引用指向子类对象”,并且,有继承、有重写,这就是多态。
每一个创建出来的对象都有this引用,指向他自身
第五步, l1.petEnjoy();
l2.petEnjoy();
调用Lady的petEnjoy()方法,输出对应信息,最终输出结果为
程序三:数组
3.1 一维数组
public class Test { public static void main(String[] args) { Date[] days = new Date[3]; for(int i=0;i<3;i++) { days[i] = new Date(2020,4,i+1); } } } class Date { int year,month,day; Date(int y, int m, int d) { year = y; month = m; day = d; } }
内存分析图
days指向堆中的一个数组,该数组中存放的是每一个Date对象的地址,每一个地址指向一个Date对象。(详细赋值过程可参考程序一)
3.2 二维数组
二维数组实际上是一维数组的数组
public class Array { public static void main(String[] args) { int[][] a = new int[][] { {1,2,3}, {4,5,6}, {7,8,9} }; } }
内存分析图
栈空间中a指向一个一维数组,该数组每块区域存放的是int型数组的地址。
笔者创作初期,如有不当之处,望批评指正。
这篇关于Java 内存分析(程序实例),学会分析内存,走遍天下都不怕!!!的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27消息中间件底层原理资料详解
- 2024-11-27RocketMQ底层原理资料详解:新手入门教程
- 2024-11-27MQ底层原理资料详解:新手入门教程
- 2024-11-27MQ项目开发资料入门教程
- 2024-11-27RocketMQ源码资料详解:新手入门教程
- 2024-11-27本地多文件上传简易教程
- 2024-11-26消息中间件源码剖析教程
- 2024-11-26JAVA语音识别项目资料的收集与应用
- 2024-11-26Java语音识别项目资料:入门级教程与实战指南
- 2024-11-26SpringAI:Java 开发的智能新利器