写在前面
今天还是写Java啊,毕竟js不知道写什么,主要是最近没做什么项目,也没什么新的技术引进来,即使有新的技术引入,自己没学明白之前也不敢写博客,不是误人子弟吗,哈哈,今天还是写老本行-Java
今天要写的呢是一个很多人头疼的问题,就是java的代码我知道怎么运行的,debug模式一打开,对吧,直接下一步下一步的走就行了,可以清楚的看到每一步的执行情况是什么样子的,这个是没什么问题的,但是往往面试的时候面试官不会问你这些代码是怎么走的,而是问你他在内存中的执行情况,那其实就是内存分析,所谓的内存分析呢其实就是代码每一部分在内存中的存放位置,调用情况,执行情况,那么了解这些了以后呢,我们就可以做一个简单的内存分析,可能你们在很多书籍里面看到过很多的内存分析的例子,不强求,觉得已经很明白的不用看了,毕竟我是一个菜逼,写不出什么有深度的东西。将就着看,给那些比我稍微弱一点的人一点启发。
下面我们写几个简单的类:
学生类(万年不变的一个经典的例子)
package studyBymyself;
/**
* 用于csdn的java代码内存分析 学生类
* @author clearlove
*
*/
public class Student {
//静态数据
String name;
int age;
int id;
String gender;
int weight;
Computer computer;
//动态的行为
public void study() {
System.out.println(name+"在学习");
}
public void sayhello(String sname) {
System.out.println(name+"向"+sname+"说你好");
}
public static void main(String[] args) {
//当我们new 一个新的对象的时候,String类型的默认是null,int的是0,布尔类型的是false,但是false本质也是0,char是\u0000,其实也是0.
//局部变量系统是不给初始化的,只有全局变量
//通过类加载器加载student类,加载后就有了sudent类的信息
Student s1 = new Student();
s1.name = "clearlove";
s1.study();
Student s2 = new Student();
s2.name = "小明";
s2.sayhello("小王");
}
}
电脑类:
package studyBymyself;
/**
* 测试内存分析 电脑类
* @author clearlove
*
*/
public class Computer {
String brand; //品牌
int cpuSpeed; //cpu速度
}
测试类1:
package studyBymyself;
/**
* 测试内存分析的测类1
*/
public class Test1 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "clearlove";
s1.study();
Student s2 = new Student();
s2.name = "小明";
s2.sayhello("小王");
}
}
测试类2:
package studyBymyself;
/**
* 测试内存分析的例子 测试类2
* @author clearlove
*
*/
public class Test2 {
public static void main(String[] args) {
Student s1 = new Student();
s1.name = "clearlove";
s1.age = 18;
Computer c = new Computer();
c.brand = "雷神";
c.cpuSpeed = 150;
s1.computer = c;
System.out.println(s1.name+"的电脑是:"+s1.computer.brand);
}
}
这里我们为了更好的做分析,首先我们分为几个大的部分。
第一部分:类 Test1,Test2,Computer,Student
第二部分:对象,instance也可以 s1,s2,c
第三部分:变量 name,age,id,gender,weight,computer,brand,cpuSpeed
第四部分:方法 study() sayhello(sname)
第五部分:常量 “在学习”,"说你好","小明","小王","雷神"...
前面说了,内存分析就是代码的每一部分在内存中放置的位置以及各个之间的调用和执行的情况,那么我们开始:
我们对Test2进行分析,程序的入口嘛,当然你分析Test1也是一样的,只是那个比较简单,分析Student也是可以的,只是也比较简单,我们分析一个最复杂的,ok,看到我们的main函数就知道程序是开始走了。
好吧,我忘记了,没开始走之前呢我们先简单的了解一下内存的组成部分,这个不说的话,下面的分析好像是没有什么实质性的意义,
内存呢我们分为堆和栈,我们分开说:
栈:可以自动分配连续的空间,具有后进先出的特点,一般用来存放局部变量
堆:数据是不连续的,放置的是new出来的对象,也就是创建的对象,那么除了局部变量以外呢,别的都放这里,再细分呢,堆又包括方法区和非方法区。
方法区主要是放置一下代码信息,静态变量和一些常量。非方法区主要放置的是对象(也就是类包含的对象)
可能有人到这里就不想看了,妈的,这是什么啊,全是概念,还不如看书呢!好吧,我承认是我写的有点多,下面我们直接对代码进行逐步分析。
我们都知道代码是从上外下,从左往右执行的,那么我们就知道一句一句的说!
第一步:
Student s1 = new Student();
这是第一步的代码,走到Student的时候,我们伟大的JVM先生就会在Class中找这个名字是Student类,看看是不是存在,如果不存在怎么办呢?报错啊,还能怎么办,存在的话,就直接在我们堆中放置了,那么这行代码在内存中是怎么放置的呢?我们用oneNote画一下:
这是第一步走完以后内存里面的分配情况,我们这里对应一下,是不是对的,前面说了,方法区里面放置的是类的信息,是吧,栈里面是放局部变量,什么是局部变量呢?s1有人说是一个对象啊,不是局部变量啊,这里说一下,有一句话说的好,万物皆对象,是不是,那么s1是不是一个变量,那又是在main函数里面,也就是方法里面那么他就不是全部变量,所以他是一个局部变量,自然就在栈里面放置,非方法区我们说放置的是对象,类的对象,怎么看是不是类的对象呢,很简单,在类里面的都属于类的对象,所以这里的变量也好,方法也好都是统一放在非方法区里面的,为什么我上面写的是null和0呢?我们都知道我们声明一个变量的时候,如果是全局变量的话,那么java虚拟机会给我们赋一个默认值,字符串的是null,数字的是0,布尔的是false(其实false也是0,1是true),char的是\u0000(其实还是0)所以这里是null和0的初始值。
下面两句:
s1.name = "clearlove";
s1.age = 18;
这里是怎回事呢?
这里就会按照地址来找对用的对象,这里说一下,所有的参数之间的调用本身是地址之间的传递,所以说其实本质是地址来定位的目标值。
下一句:
Computer c = new Computer();
和之前的一样:
这里也是一样的行为,不解释了。
下面一句:
c.brand = "雷神";
c.cpuSpeed = 150;
内存中怎么表示呢?
下一句就比较有意思了:
s1.computer = c;
这里我们可以看到,是将Computer的实例化的c赋值给了Student的实例化对象的对象,是不是有点绕,慢慢读就知道了。内存怎么表示呢?
这里呢就是说原本找到brand是需要c.brand的,为什么呢?前面说了,brand属于类Computer的,那c又是类Computer 的实例化变量,自然可以找到,那现在c将自己的地址给了Student的实例化对象的对象,Computer这个类本身又是Student类的一个对象,所以呢,我们找brand就有两种办法了,第一种是:c.brand,第二种是:s.computer.brand,就好比说,你找小明的书包,别人告诉你他知道小明爸爸的儿子的书包在哪里,那不还是小明的书包嘛!