设计jvm的背景:

机器码是 CPU 可以直接解读的指令,机器码肯定是与 底层硬件系统辑合的。
某个程序因为不同的硬件平台就需要编写多套代码,非常麻烦,那如何实现一套代码跨平台执行?
就是通过字节码文件被JVM识别为机器码并交给cpu执行。
从jdk下载来看,不同的硬件平台、操作系统对应一种jvm版本
image.png

jvm组成

下面这个图是jvm系统流转的过程,包括几个系统
1:类加载系统
装载class文件到运行期数据区
2:运行时数据区
包括虚拟机栈、本地方法栈、程序计数器、堆、元空间
3:本地接口
和本地方法库交互
4:执行引擎
字节码文件只是jvm的一套指令集,并不能直接交给底层操作系统去执行,因此需要特定的执行引擎将字节码翻译为底层系统指令,再交给cpu执行。

image.png

1:类加载系统

类加载是一个将 .class 字节码文件实例化成 Class 对象并进行相关初始化的过程。
字节码文件的结构:
image.png

image.png
load阶段为了保护java核心类库的安全有个双亲委派机制,
image.png
一个tomcat可以加载多个webApp, webApp可能有多个spring版本,所以它在双亲委派的基础上也可以自定义类加载器。

2:运行时数据区

元空间(jdk1.8)

刚说的class文件主要是被分配到了元空间和堆里了
元空间在本地内存中分配,包含运行时常量池,类元信息、方法信息
字符串常量池是分配在堆中

栈( Stack )是-个先进后出的数据结构
每个方法从开始调用到执行 完成的过程 , 就是栈帧从入栈到出栈的过程。在活动线程中,只有位于栈顶的帧才是 有效的,称为当前栈帧
image.png

  • 局部变量表是存放方法参数和局部变量的区域。
  • 操作数栈:在方法执行过程中, 会有各种指令往 枝中写人和提取信息。

JVM 的执行引擎是基于栈的执行因擎,其中的栈指的就是操作数栈。

  • 动态链接:在常量池中对当前方法的引用,支持方法在调用过程的动态链接
  • 方法返回地址:方法退出的过程相当于弹出当前栈帧

程序计数器

每个线程在创建后,都会产生 自己的程序计数器和栈帧,
程序计数器用来存放执行指令的偏移量和行号指示器等, 线程执行或恢复都要依赖程序计数器

不同的垃圾处理器,对于堆的物理分配不同。
parNew+cms:
image.png
G1:
image.png
他们的相同之处是都保留了分代思想。

对象创建过程

  1. 类加载检查

先在元空间检查需要创建的类元信息是否存在,不存在就执行类加载的流程(第一课里详细说了)
(2)分配内存
分配内存的方法是 指针碰撞(内存规整),空闲列表(内存不规整)
分配到哪个区域呢?
先进行逃逸分析,能在栈上分配的话通过标量替换方法,不行的话就分配到堆中,
具体堆中的分配位置,先判断tlab,栈在堆的eden区预先分配的内存能否放下去,
放不下的话如果是大对象就到老年代,
eden区没有放满的话,就放到eden区。如果刚好填满eden区,还要判断是否触发老年代担保机制。
如果触发young gc后,到了幸存区还会判断动态年龄判断机制
image.png
(3)设定成员变量的零值
(4)设置对象头
设置新对象的晗希码、 GC 信息、锁信息、对象所属的类元信息等。
(5)执行初始化方法
初始化成员变量, 调用类的构造方法,并把堆内对象的地址赋值给引用变量。

垃圾回收

三色标记:
垃圾回收算法:
垃圾回收过程:
漏标的解决方法:1:增量更新(cms) 2: 原始快照(g1)