1 概念
JVM是运行java的假想计算机
包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收、堆、一个存储方法域
各个平台虽然解释器不同,但是实现的虚拟机相同,所以java能够跨平台
2 运行
java源文件 -> 编译器 -> 字节码文件 -> JVM -> 机器码
程序启动,虚拟机开始实例化,程序启动,虚拟机实例消亡
2.1 线程
Hotspot JVM的java线程,与原生操作系统线程直接映射:当线程本地存储、缓冲区分配、同步对象、栈、程序计数器等都准备好之后,就会创建一个操作系统原生线程。Java线程结束后原生线程随之被回收,操作系统调度所有线程并分配可用CPU。原生线程初始化完成会调用java线程的run方法,线程结束后释放原生线程和java线程的所有资源。
Hotspot jvm的系统线程
线程类型 | 说明 |
---|---|
虚拟机线程 | jvm到达安全点操作时出现,在独立的线程中执行。这些操作类型有:stop-the-world垃圾回收、线程栈dump、线程暂停、线程偏向锁解除 |
周期性任务线程 | 负责中断,调度周期性操作的执行 |
GC线程 | 不同的垃圾回收活动 |
编译器线程 | 将字节码动态编译成本地平台的机器码 |
信号分发线程 | 接受发送到JVM的信号并调用jvm的方法处理 |
2.2 JVM内存区域
包括:
- 线程私有的:
- 程序计数器PC:无oom
- 虚拟机栈:存放数据和部分过程结果、处理动态连接、方法返回值、异常分派
- 本地方法栈:类似于虚拟机栈,为Native方法服务
- 线程共享
- 方法区(永久代):jvm加载的类信息、常量、静态变量、JIT的代码等(hotspot用永久代实现了方法区,可垃圾回收)
- 类实例区(java堆):新生代、老年代
- 直接内存
2.3 JMV运行时内存
从GC的角度划分为新生代(eden区 8/10、from survivor区 1/10、to survivor区 1/10)、老年代
永久代:存放class、meta信息,class加载时被放入永久区域,运行期间不会被GC
java8的永久代
被移除,新增“元数据区/元空间”,使用本地内存native memory
类的元数据放入本地内存,静态变量放入堆中
2.4 类加载机制
加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
-
加载:在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的入口。
-
验证:确保class文件的字节流中包含的信息是否符合当前虚拟机的要求
-
准备:在方法区中分配变量所使用的内存空间并设置类变量的初始值。(static int v = 8080的定义,变量初始化为0,v的赋值被编译到类构造器方法中,static final int v = 8080,初始化为8080,编译时给v生成了ConstantValue属性
-
解析:虚拟机把常量池中的符号引用替换为直接引用
符号引用:引用的目标不一定都加载到内存了,但是能接受的符号引用是一样的,符号引用的字面量形式定义在java虚拟机规范的class文件格式中
直接引用:引用目标必定在内存中,是指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄 -
初始化:执行java代码
类构造器<client>
:初始化执行,该方法由编译器自动收集类中的变量赋值操作和静态语句块中的语句合并而成,类中如果没有静态变量赋值及静态语句块,那这个类可能不用生成<client>
方法,虚拟机保证子<client>
方法在父<client>
之后执行
2.5 类加载器
JVM提供三种类加载器
- bootstrap classloader:JAVA_HOME\lib下的 + -Xbootclasspath目录下且被虚拟机认可的jar(根据文件名识别)
- extension classloader:JAVA_HOME\lib\ext下的 + java.ext.dirs变量指定的
- application classloader:用户路径
application classloader的加载,通过双亲委派模型加载
即:一个类收到类加载请求,不会尝试自己去加载,会把请求委托给父类,每一层类加载器都这样,直到父类反馈无法完全请求,子类加载器才会尝试自己去加载
保证了同一个类由不同classloader加载都得到同样一个object
2.6 OSGI 动态模型系统
open service gateway initiative
面向java的动态模型系统,是java动态化模块化系统的规范
- 多种网络设备上无需重启的动态改变构造,提供一种面向服务的架构
- 可能实现模块级的热插拔功能(不遵守类加载的双亲委托模型)