概述
什么是 Java 虚拟机运行时数据区?
第一个问题:什么是 虚拟机?
定义:[运行在操作系统上的具有物理机拥有的全部硬件功能的软件]{.red}
核心:[虚拟机采用软件对真实计算机进行模拟]{.red}
:::info
注:既然是对真实计算机的模拟,那么虚拟机中的很多组件显然会对应真实计算机中的硬件
:::
第二个问题:什么是 Java 虚拟机?
定义:简单来说就是运行 Java 进程的虚拟机
:::info
[① 这种说法并不严谨,只要能够通过编译生成字节码的程序都是可以在虚拟机中运行的]{.green}
[② 诸如 Kotlin、Clojure 等语言也都是运行在 Java 虚拟机上的]{.green}
:::
核心:Java 为了实现跨平台的特性而特意开发的虚拟机,使其能够不受硬件的制约
细节:[每个 Java 进程都运行在相应的 JVM 实例上]{.red}
:::info
注:翻译一下就是说每个 Java 进程都对应一个 JVM 实例,多个 Java 进程就会存在多个 JVM 实例
:::
第三个问题:什么是运行时数据区?
- 定义:简单来说就是虚拟机中 [存放 Java 进程各种各样数据的空间]{.red}
- 细节:真实计算机中进程必须加载到内存中才能够执行,同样在 JVM 中进程也需要加载到运行时数据区中才能够执行
第四个问题:运行时数据区由哪些部分组成呢?
运行时数据区总结图示

内存模型简化图示
:::warning
运行时数据区并不包含执行引擎部分,这里只是为了能够展示整个虚拟机全貌而画出来的
:::

内存模型完全图示
线程
:::warning
① 本部分不会介绍 Java 线程的基础知识,更加不会介绍 Java 线程并发的知识,放在另外的博客介绍
② 本部分也不会像操作系统那样详细介绍线程的来龙去脉,这部分已经在操作系统的博客中介绍过了
③ 本部分主要从操作系统线程的角度来看待 Java 线程
参考书籍:《深入理解 Java 虚拟机》
建议阅读:线程概述
:::
- 前提:具备操作系统层面的相关知识之后再阅读如下部分
Java 线程实现
Java 线程历史
- Java 在 JDK 1.2 时期的 Classic 虚拟机上采用线程实现方案是 [用户级线程实现]{.blue}:称为绿色线程(Green Threads)
- Java 在 JDK 1.3 时期时,所有主流的商用虚拟机全部采用 [内核级线程实现]{.blue}
- Java ME 的 CLDC-HI 虚拟机采用的是两种实现方案: [用户级线程实现 + 混合级线程实现]{.blue}
- Solaris 的 HotSpot 虚拟机采用的也是两种实现方案:[内核级线程实现 + 混合级线程实现]{.blue}
:::info
① Java 早期采用用户级线程的目的应该是比较明确的:为了实现更好的跨平台;改用内核级线程估计还是处于性能上的考虑
② Java 之后采用内核级线程就导致了各个操作系统上的虚拟机的具体实现根本不一样,Solaris、Windows、Linux等等,都有自己的实现方案
:::
Java 线程调度
:::warning
建议阅读:调度算法
:::
前提:最好具备一定的操作系统的调度算法知识再继续阅读
引入:多线程并发执行时,如何安排这些线程执行呢?这就是调度算法要完成的工作
分类:
- 协同式调度(Cooperative Threads-Scheduling)
- 名称:协同式调度也可以称为非抢占式调度
- 定义:线程只有执行完成自己的所有任务后主动放弃处理器的使用权
- 优点:调度算法实现简单
- 缺点:当前线程执行时间过长会导致后续所有线程长时间等待,即使后续线程执行时间很短
- 抢占式调度(Preemptive Threads-Scheduling)
- 定义:其他线程可以抢占当前线程处理的使用权,无论当前线程是否执行结束
- 优点:避免线程长时间等待,线程执行效率高
- 缺点:调度算法实现复杂
- 协同式调度(Cooperative Threads-Scheduling)
[Java 采用抢占式类型的调度算法]{.red}
- 此前提到 Java 采用的线程实现方案是内核级线程实现,所有线程的控制权都在操作系统手上,JVM 没有管理的权利
- 这里又提到 Java 采用的调度算法是抢占式的,不同的线程在交替执行
- 这就出现了多线程编程中的常见现象
- 每次启动多个线程,执行的先后结果都是不一样的:每次抢占的情况都不一样
- 设定线程优先级后,依然没有按照预想的情况执行:JVM 仅能给操作系统建议,而无法控制
Java 优先级:
Java 提供了 10 种优先级(1~10):主线程默认优先级为 5
Windows 操作系统也提供了 7 种优先级
Solaris 操作系统提供了 2^31 种优先级
+++danger 问题来了,Java 和操作系统的优先级数量不一样,那么是怎么对应上的呢?
实际很简单,如果操作系统的优先级数量少,那么 Java 中多个优先级就对应一个就行了,如 Windows
所以得到结果就是,Java 中1和2、3和4、6和7、8和9的效果是完全相同
+++
Java 线程状态转换
Java 线程状态:
初始态(New):线程对象被创建但是没有启动时的状态
可运行态(Runnable):包含 [线程准备运行和线程正在运行]{.red} 两种状态,即对应操作系统中两种状态
等待态:线程正在等待
无限等待(Waiting):线程必须被 [其他线程唤醒]{.red},否则该线程永远不会执行
// 以下方法会导致线程进入无限等待
wait()
// 唤醒方法
notify()/notifyAll()有限等待(Timed Waiting):线程睡眠一段时间后 [自动唤醒]{.red},不需要其他线程帮助
// 以下方法会导致线程进入有限等待
sleep()
阻塞态(Blocked):线程等待其他线程释放同步锁
终止态(Terminated):线程执行结束或者意外终止
:::info
① Java 定义线程运行状态和操作系统层面定义的有些细微差距,要注意区分
② Java 线程的状态是可以测试得到的,之后在其他博客中说明
:::

Java 与协程
- 前提:[目前 Java 并没有提供协程技术]{.blue}
Java 守护线程
- 虚拟线程
- 周期任务线程
- 垃圾回收线程(GC线程)
- 编译线程
- 信号调度线程
字节码解析
前提:后续的学习中会涉及到字节码的分析,而字节码的解析途径有多种,在此提前解释下
反编译:定义:将字节码文件解析成 Java 源代码
IDAE:本身就可以直接反编译字节码文件
点击进入 out/production/ 目录下(存放所有字节码文件的目录)
[实际上源代码并不是我们想要查看的信息,我们需要把字节码解析成另外的形式才能得到更多的信息]{.green}
:::warning
注:毕竟我们自己编写的源代码,再去查看一遍确实没有什么意义
:::
反编译命令
选择命令行终端
:::info
IDEA 或者 Windows 中使用都是可以的,笔者这里使用 IDEA 里的终端
:::
点击 IDEA 中的终端后切换当前所在目录
# 后面接上你自己的字节码所在的目录
cd out/production/yourdirectoryname使用反编译命令解析字节码文件:
# Java 自身提供的反编译指令:接上你想要反编译的类的名称
javap -v ClassName.class反编译解析结果:可以先简单了解下
反编译插件:
- IDEA 中安装 Jclasslib 插件
- 选中想要解析的字节码文件后点击 View 选中 Show ByteCode With Jclasslib
- 就可以非常方便的查看字节码文件解析后的结果