文章目录
一、运行时数据区
在装载阶段的第(2),(3)步可以发现有运行时数据,堆,方法区等名词
(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
(3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口 。说白了就是类文件被类装载器装载进来之后,类中的内容(比如变量,常量,方法,对象等这些数据得 要有个去处,也就是要存储起来,存储的位置肯定是在JVM中有对应的空间)
1.1 图解
1.2 初步认识
1.2.1 Method Area(方法区)
- 方法区是各个线程共享的内存区域,在虚拟机启动时创建
- 虽然JVM规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的是与Java堆区分开
- 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
- 当方法区无法满足内存分配需求时,抛出OutOfMemoryError异常
1.3.2 Heap(堆)
- Java堆是JVM管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享
- Java对象实例以及数组都在堆上分配
1.3.3 JVM Stacks(虚拟机栈)
- 虚拟机栈是一个线程执行的区域,保存着一个线程中方法的调用状态。一个Java线程的运行状态,由一个虚拟机栈来保存,所以虚拟机栈是线程私有的
- 每一个被线程执行的方法,为该栈中的栈帧,即每个方法对应一个栈帧。调入一个方法,就会向栈中压入一个一个栈帧;一个方法调用完成,就会把该栈帧从栈中弹出
- 图解栈和栈帧
void a(){
b();
}
void b(){
c();
}
void c(){
}
- 栈帧
栈帧:每个栈帧对应一个被调用的方法,可以理解为一个方法的运行空间
每个栈帧包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向运行时常量池的引用、方法返回地址(Return Address)和附加信息
局部变量表:方法中定义的局部变量以及方法的参数存放在这张表中
操作数栈:以压栈和出栈的方式存储操作数的
动态链接:每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接
方法返回地址:当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇 见异常,并且这个异常没有在方法体内得到处理
1.3.4 程序计数器
程序计数器,记录线程执行到的位置;如果线程正在执行Java方法,则记录正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,则这个计数器为空
1.3.5 本地方法栈
如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行
1.4 折腾一下
1.4.1 栈指向堆
如果在栈帧中有一个变量,类型为引用类型,比如Object obj = new Object(),这就是典型的栈中元素指向堆中的对象
1.4.2 方法区指向堆
方法区中会存放静态变量、常量等数据。比如private static Object obj=new Object(),就是典型的方法区中元素指向堆中的对象
1.4.3 堆指向方法区
1.4.4 Java对象内存模型
一个Java对象在内存中包含3个部分:对象头、实例数据和对齐填充
二、 JVM内存模型
2.1 与运行时数据区
- 堆和方法区是线程共享的
- 虚拟机栈、本地方法栈、程序计数器都是线程私有的
可以换句话来说,JVM运行时数据区是一种规范,而JVM内存模式是对该规范的实现
2.2 图形展示
2.3 对象创建过程
新创建的对象会被分配到Eden区,一些特殊的大的对象会被直接分配到Old区
我是一个普通的Java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的。
2.4 常见问题
- 如何理解Minor/Major/Full GC
Minor GC:回收全部新生代
Major GC:回收全部老年代
Full GC:回收全部
- 为什么需要Survivor区?只要Eden区不行吗?
Survivor存在的意义是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选保证,只有经历16次Minor GC还能在新生代中存活的对象,才会被送到老年代
- 为什么需要两个Survivor区?
是为了解决碎片化,永远有一个Survivor space是空的,另一个非空的Survivor space无碎片
- 新生代中Eden:S1:S2为什么是8:1:1?
新时代中有98%的对象都会被回收,只会剩下2%,所以使用10%的内存作为Survivor是绰绰有余的
- 堆内存中都是线程共享的区域吗?
不是,JVM会默认为每个线程在Eden上开辟一个buffer区域,用来加速对象的分配,称之为TLAB,对象会优先在TLAB上分配,但是TLAB空间通常会比较小,如果对象比较大,那么还是会在共享区域分配