本节讨论一下内容的结构
- 堆
- 栈
- 方法区
栈
定义: 随线程创建而生,线程销毁而灭.
存放内容:
- 方法内定义的局部变量(基本类型存内容,引用类型存引用。不存引用类型的具体内容)
- 方法参数
JVM 规定栈的内容:
- JAVA虚拟机栈 (所有非native方法的栈)
- 本地方法栈(所有native方法的栈)
- 程序计数器(程序运行的字节码行号指示器,可以理解为DEBUG 断点时,当前的行号)
JAVA虚拟机栈
栈是一种数据结构,具有先进后出的特性。JAVA虚拟机栈就是一个栈结构的一块数据区
搞清楚JAVA虚拟机栈 push和pop 的内容是什么,也就理解了JAVA虚拟机栈
方法被调用时,栈帧入栈. 方法返回时,栈帧出栈
栈帧是什么呢? 存放了什么东西
- 局部变量表(可以理解为 定义的局部变量)
- 操作数栈(执行指令的参数)
- 动态链接(运行时常量池的方法引用. 可以理解为java.lang.Method 对象)
- 返回地址(return)
这些概念还是很拗口,很难理解。 接下来通过例子来加深对栈帧的理解
举例说明:
public class JvmStack {
public static void main(String[] args) {
methodA();
}
public static void methodA() {
int a = 1;
methodB(a);
}
public static int methodB(int param) {
return param;
}
}
这里省略了main方法栈帧
入栈出栈图
methodA方法被调用时, methodA 栈帧入栈
methodB方法被调用时, methodB 栈帧入栈
methodB 调用结束后.栈帧恢复为 methodA方法被调用
的形态
methodA 调用结束后.栈帧恢复为 main方法被调用
的形态
堆
堆是JAVA内存结构中,最复杂的部分,垃圾收集也主要
是针对这一块区域
定义:
堆的主要作用就是进行对象内容的分配.
存放内容: new对象的实际占用空间
堆的划分
JAVA堆被划分为两块区域
- 新生代
- 老年代
他们是物理隔离的两块区域(G1收集器时除外)
堆为什么要划分为新生代,老年代?
由于JAVA分配对象的特性,98%的对象都是
朝生夕死
的, 将堆划分为两块区域是
为了采用不同的垃圾收集算法收集相应的区域。
新生代对象分配
划分新生代之后,分配对象时,内存是绝对规整的,可以用拨动指针的方式分配对象
假设从 0x46F03000的位置开始分配对象
- 第一个对象:0x46F03000, 大小为1MB
- 第二个对象: (0x46F03000+1MB), 大小为13KB
- 第三个对象起始地址: (0x46F03000+1MB+13KB),大小为105KB
对象的大小只要是 8 的整数倍就行.(否则会加入对齐填充)
老年代对象分配
老年代的内存并不是绝对规整的
如图。 即将被回收的对象回收后。 这个缝隙只能容得下新对象A
新对象B分配时要找可以容纳自己的位置
堆内存总结
堆将区域划分为多块的目的: 为了更高效的垃圾收集
方法区
存放内容:
- 类的Class对象
- 运行时常量池(可以理解为定义的常量)
JDK1.8 之后将PermGen 改为 Metaspace。 也叫做元数据区