Java虚拟机的内存大致分为三个区域:
- 栈stack:
- 栈是方法执行的内存模型,每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等),每个方法执行的相关调用都在栈里面;
- 栈是线程私有的,不能线程共享;
- 虚拟机会为每个线程创建一个独立的栈;
- 栈的存储特性类似于子弹的弹夹,先进去的后出来,后进去的先出来,如方法一调用方法二,方法二调用方法三,先开辟方法一的栈帧,再开辟方法二的栈帧,最后开辟方法三的栈帧,但是执行的关闭顺序是先关闭方法三的栈帧,再关闭方法二的栈帧,最后关闭方法一的栈帧,而当整个线程执行完时,该线程的栈空间就会被关闭;
- 栈由系统自动分配,是一个连续的内存空间。
- 堆heap:
- 堆是用于存储创建好的对象和数组(数组也是对象);
- 堆空间是唯一的,被所有线程共享;
- 堆是一个不连续的内存空间,分配灵活,速度慢。
- 每当我们new一个对象的时候,都会在堆里面有一个该对象的创建。
- 方法区method area:方法区实际上也在堆里面,但是相较于其他比较特殊,所以单独提出来。
- 方法区也叫静态区,且是唯一的,被所有线程共享;
- 方法区实际上也是堆,只是用于存储类的代码信息(如类信息的【Class对象】,静态变量,字符串常量等),即用来存放程序中永远是不变或唯一的内容;
实际分析:
以下是一个demo,我们来结合demo分析:
demo类:
package com.lr;
/**
* @className Demo
* @Description TODO
* @Author RJin
* @Date 2020/5/23 11:36
* @Version 1.0
*/
public class Demo {
public static void main(String[] args) {
Student stu = new Student();
stu.id = 1;
stu.sname = "R";
stu.age = 18;
Computer computer = new Computer();
computer.brand = "戴尔";
stu.computer = computer;
stu.play();
stu.study();
}
}
Student类:
package com.lr;
/**
* @className Student
* @Description TODO
* @Author RJin
* @Date 2020/5/23 11:36
* @Version 1.0
*/
public class Student {
int id;
String sname;
int age;
Computer computer;
public void play(){
System.out.println("学生们在操场上玩");
}
public void study(){
System.out.println("学生们在教室里学习,使用电脑:"+computer.brand);
}
}
Computer类:
package com.lr;
/**
* @className Computer
* @Description TODO
* @Author RJin
* @Date 2020/5/23 11:44
* @Version 1.0
*/
public class Computer {
String brand;
}
首先,虚拟机调用demo字节码文件,创建栈,堆,方法区:
执行main方法。在栈中创建栈帧:
new Student()调用Student类的构造方法,创建Student类无参构造方法栈帧:
会在堆中创建一个对象,并且该对象会在堆中有一个地址,这个地址并不固定,每次对象创建都会发生改变,可以通过System.out.println(stu)来打印:
接着该方法执行完毕后会关闭它所对应的栈帧,将在堆中创建的对象引用赋给main方法中Student类的stu对象:
接着给stu对象的属性赋值:
接着执行computer对象的创建,先在main方法中创建一个computer对象:
接着开辟computer无参构造方法的栈帧,在堆中创建一个对象,并返回该对象的引用给main中的computer对象:
stu.computer = computer;就是将堆中地址为12fb56的computer对象的引用赋给stu对象的computer对象属性:
执行完stu对象的study方法、play方法后,关闭main方法栈帧,虚拟机关闭,所有资源销毁。