JVM内存模型图

(图1-1)
内存模型分为5块区域
- 虚拟机栈
- 本地方法栈
- 堆
- 方法区(元空间)
- 程序计数器
首先:假设有一个Hello.java类,通过javac 编译为Hello.class字节码文件,通过类装载子系统,加载进JVM虚拟机中
方法区
方法区会加载类的常量,静态变量,类元信息(方法名,修饰符等等)
虚拟机栈
JVM会为每一个线程在虚拟机栈中开辟一小块区域,存放线程中的数据(程序计数器和方法栈帧),栈是先进后出,对于执行引擎来说,只有栈顶的栈帧是有效的,被称为当前栈帧,所关联的方法被称为当前方法
栈帧中又分为四块区域
- 局部变量表:是一组用于存放方法参数和方法内局部变量的存储空间,索引0位置,存放this,也就是当前对象地址
- 操作数栈:操作数在程序运行中临时存放数据做运算的一块区域
- 动态链接:将符号引用在程序运行中转化为直接应用,也就是说,一个方法在调用另一个方法的时候,需要知道另一个方法的信息,栈中存有堆中对象的引用地址,而堆中对象是根据方法区中的类信息所创建,也就可以找到另一个方法的信息,达到调用的目的
- 方法出口:存放调用该方法的程序的位置((图1-2)code列的位置),结束当前方法的时候,可以回到上一方法,继续运行
PS:javap -c指令可以对class文件进行反编译,生成如图文件,可以看出底层代码执行顺序,程序计数器中存放就是code列的值,通过值找到程序运行的位置
Java字节码指令大全

(图1-2)
本地方法栈
Java调用本地native方法的时候会将方法信息压入本地方法栈
堆

新建对象都会放入堆区中Eden,堆区的对象都是线程共享的。
堆被划分为新生代和老年代两个区域,其中新生代又被划分为Eden,From survivor ,
To survivor 三个区域
老年代占据2/3,新生代占据1/3
新生代中 Eden 占据 8/10,From survivor 和 To Survivor 各自占据 1/10
这样划分是为了JVM能够更好的管理堆内存中的对象
对象分为对象头和实例数据
对象头

堆是内存分配中最大的一块区域,也是GC重点关注的区域,GC回收前要判断堆中哪些对象存活,哪些不可能再被引用,需要回收。GC分为两种 minor GC 和 Full GC。
minor GC为新生代收集垃圾,Full GC为老年代收集垃圾
minor GC会在Eden区满的时候被触发,清理没有被GC Root引用链所引用的对象
剩余存活的对象,通过复制算法,进入survivor区中的From,并且对象头中的分代年龄+1,
第二次再触发时,清理Eden和From区中的对象,仍然通过复制算法,把两区域中存活的对象迁移到To区,分代年龄+1
第三次触发,会把Eden区和To区存活对象迁移到From区,分代年龄+1
Java中的对象通常不会长久存活,所以需要GC负责回收,新生代是GC回收最频繁的区域。
分代年龄达到15的对象,会被移动到老年代(spring管理的Bean,程序初始化时的一些对象)
--一些较大的对象,可能会直接被放入老年代
老年代被放满之后会停掉程序线程,去执行Full GC,老年代的GC采用标记-清除算法
ps:JDK自带jvisualvm指令,可以打开 Java VisualVM,jvisualvm安装visual GC插件可以对程序运行中的GC进行分析,图像化直观展示
GC 算法
JAVA中使用可达性分析来判断对象是否存活
可达性分析:以若干个GC Root为根节点,从节点向下搜索,如果一个对象没有被若干条引用链所引用到,则证明这个对象不可达。
GC Root根节点:线程栈的本地变量,静态变量,本地方法栈的变量
