Java虚拟机的内存可以分为三个区域:栈stack、堆heap、方法区(静态区)method area。
栈的特点如下
- 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等)
- JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
- 栈属于私有线程,不能实现线程间的共享
- 栈的存储特性是“先进后出,后进先出”
- 栈是由系统自动分配,速度快,栈是一个连续的内存空间
堆的特点如下
- 堆用于储存创建好的对象和数组(数组也是对象)
- JVM只有一个堆,被所有线程共享
- 堆是一个不连续的内存空间,分配灵活,速度慢
静态区的特点如下
- JVM只有一个静态区,被所有线程共享
- 静态区实际也是堆,只是用于存储类、常量相关的信息
- 用来存放程序中永远不变或唯一的内容(类信息【Class对象】、静态变量、字符串常量等)
面向对象内存分析
下面通过代码来对面向对象的内存模型进行分析。创建Computer类,里面有一个brand(品牌)属性
public class Computer {
String brand;
}
然后创建Student类
public class Student {
int id;
int age;
String name;
Computer computer; // 电脑
static String address = "上海";
void study() {
System.out.println("我再认真学习,使用电脑:" + computer.brand);
}
static void sayAddress() {
System.out.println(address);
}
public static void main(String[] args) {
Student student = new Student();
student.id = 1001;
student.name = "gavin";
student.age = 18;
Computer computer = new Computer();
computer.brand = "联想";
student.computer = computer;
student.study();
}
}
当程序运行后,内存示意图如下
上图中
- 静态区的内存空间会最先分配,它的分配是在程序开始编译时完成的
- 对象都是在堆中开辟并分配内存,栈只是持有对象内存地址的引用
- 每个方法被调用都会创建一个栈帧,main方法以及main方法中的new Student()、new Computer()都会创建一个独立的栈帧
基本类型变量分配在堆还是栈?
通过上面的分析我们已经知道对象(也即引用类型变量)的内存都是分配在堆中的,栈只是持有堆内存地址的引用,那基本类型变量的内存是分配在堆还是栈呢?其实堆、栈都可以为基本类型变量分配内存,基本类型变量的分配要看声明的位置
- 如果基本类型变量定义在类中(也即成员变量),那么内存会直接分配在堆中,栈使用的话只是持有该内存地址的引用
- 如果基本类型变量定义在方法中或代码块中(也即局部变量),那么内存会直接分配在栈中。
可以总结如下
局部变量 | 成员变量 | |
---|---|---|
基本数据类型 | 变量名和值都在方法栈 | 变量名和值都在堆内存 |
引用数据类型 | 变量名在方法栈,变量名指向堆内存地址 | 变量名和变量名指向的内存地址都在堆 |
如下面变量a的内存就是直接分配在栈方法中,而c1变量名分配在栈中,它实际指向堆中的computer对象内存地址
void study() {
int a = 1;
Computer c1 = this.computer;
}