要想了解Java的低层是如何运作的,更扎实的明白Java的数据存储,内存分析是必不可少的。
Java虚拟机的内存可以分为三个区域:栈,堆和方法区(实际上是2个,方法区实际上是一种特殊的堆,存在堆里面),不管是堆,栈还是方法区,都有相应的特点,存放相应的东西。
堆的特点:
1、堆用于存储创建好的对象和数组(数组也是对象)
2、JVM只有一个堆,被所有线程共享
3、堆是一个不连续的内存空间,分配灵活,速度慢!
栈的特点:
1、栈描述的是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
2、JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
3、栈属于线程私有,不能实现线程间的共享!
4、栈的存储特点是:先进后出,后进先出
5、栈是由系统自动分配,速度快!栈是一个连续的内存空间!
方法区(又叫静态区)特点:
1、JVM只有一个方法区,被所有线程共享!
2、方法区实际也是堆,只是用于存储类、常量相关的信息!
3、用来存放程序中永远是不变或唯一的内容。(类信息【Class对象】,静态变量、字符串常量等)
下面我们就举个例子,说明一下内存分析,先贴下代码,我们有简单的3个类,公司类,部门类,员工类。
公司类:
public class Company {
private String name = "我房旅居集团"; //8
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
部门类:
public class Department {
private String name;
private Company company;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
员工类:
public class Staff {
private static boolean ISANIMAL = true;
private String name;
private int age;
Department department;
public String staffInformation(){
String string = ""; //16、23
//17、24
string = "用户为:"+department.getCompany().getName()+department.getName()+name;
return string;
}
public static boolean isAnimal(){
return ISANIMAL; //19
}
//javac Staff.java java Staff //1
public static void main(String[] args) { //2
Staff staff = new Staff(); //3、4
staff.name = "林高禄"; //5
staff.age = 28; //6
Company company = new Company(); //7、8、9
Department department = new Department(); //10、11
department.setCompany(company); //12
department.setName("系统开发部"); //13
staff.department = department; //14
String iInformation = staff.staffInformation(); //15、18
System.out.println(iInformation); //19
System.out.println(Staff.isAnimal()); //20
company.setName("橙云科技"); //21
iInformation = staff.staffInformation(); //22、25
System.out.println(iInformation); //26
}
}
注:以上代码中的注释1-26,代表的是代码再内存分析中的执行步骤。
下面我们就进行内存分析:
第1步 :将java文件编译成class文件,类信息【Class对象】,静态变量、字符串常量等都存放到方法区中。
第2步:main方法的栈帧执行,staff为空
第3步:调用Staff类的构造器,先创建对象,属性赋值,后调用构造器赋值
第4步:Staff类的构造器调完,栈帧出栈,堆内内存地址赋予staff
第5步:staff通过内存地址找到name,把"林高禄"的地址赋予给staff.name
第6步:staff通过内存地址找到age,把age赋值为28
第7步:调用Company构造器
第8步:把"我房旅居集团"的地址赋予给Company类的name
第9步:Company类的构造器调完,栈帧出栈,堆内内存地址赋予company
第10步:调用Department构造器
第11步:Department类的构造器调完,栈帧出栈,堆内内存地址赋予department
第12步:把company的内存地址赋予给department的company
第13步:department通过内存地址找到name,把"系统开发部"的地址赋予给department.name
第14步:把department的内存地址赋予给staff的department
第15步:调用staffInformation方法入栈
第16步:把“”的地址赋予给staffInformation方法内部的string
第17步:通过一些列get方法加上字符串拼接,组装成新字符串,并且把新字符串的地址赋值给staffInformation方法内部的string
第18步:staffInformation方法调完,栈帧出栈,把staffInformation方法返回的字符串地址赋予给iInformation,内部的string也被回收。
第19步:打印iInformation
第20步: 调用isAnimal方法入栈,返回值,调用完栈帧出栈,打印返回的值
第21步: company通过内存地址找到name,把"橙云科技"的地址赋予给company.name(这里内存地址是不一样的,画图输入错误)
第22步:调用staffInformation方法入栈
第23步:把“”的地址赋予给staffInformation方法内部的string
第24步:通过一些列get方法加上字符串拼接,组装成新字符串,并且把新字符串的地址赋值给staffInformation方法内部的string
第25步:staffInformation方法调完,栈帧出栈,把staffInformation方法返回的字符串地址赋予给iInformation,内部的string也被回收。
第26步:打印iInformation
到这里,这个内存分析简单的介绍完了,如果有什么不对的地方,还请大家指出来。