JVM 介绍、内存概念模型、类加载机制
- 一、概述
- Graal VM
- 二、内存概念模型
- 1、程序计数器
- 2、Java 虚拟机栈
- 3、本地方法栈
- 4、Java 堆
- 5、方法区
- 6、运行时常量池
- 7、直接内存
- 三、类加载机制
- 1、类加载的时机
- 2、类的生命周期
- 3、双亲委托机制
- 4、三大加载器
一、概述
虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
得益于Java虚拟机,Java自诞生起便提出口号“Write Once, Run Anywhere”
JDK、JRE 区别:
- JDK 指整个Java技术体系
- JRE 指支持Java程序运行的标准环境
Java技术体系:
- Java程序设计语言
- 各种硬件平台上的Java虚拟机
- Class文件格式
- Java API 类库
- 第三方 Java 类库
常见虚拟机
虚拟机 | 特点 |
---|---|
Sun Classic/Exact VM | |
HotSpot VM | Java默认虚拟机,以热点探测技术而命名,热点探测能通过执行计数器找出最具编译价值的代码,并以方法为单位进行编译。 |
BEA JRocket | JRocket虚拟机以垃圾收集器、Java Mission Control故障处理套件功能而闻名于世,现已不再发展 |
IBM J9 | 一款由IBM设计,可在服务端、桌面应用、嵌入式得到全方位应用的虚拟机。其闻名于优秀的职责分离、模块化。 |
BEA Liquid | 一款不需要操作系统支持的虚拟机,它自身实现了一个专用的操作系统功能(线程调度、文件系统、网络支持),线程切换无需进行内核态/用户态 切换。 |
Azul VM | 运行于Vega系统上的虚拟机,每个实例可管理至少数十个CPU、数百GB内存,因其可在巨大内存范围内可控的垃圾收集器(PGC和C4)闻名 |
使用java -version
查看当前ava版本号、虚拟机
Graal VM
Graal VM 虚拟机的口号:“Run Program Faster Anywhere”
GraalVM ,一个高性能的通用虚拟机,可以运行使用 JavaScript,Python 3,Ruby,R,基于 JVM 的语言以及基于 LLVM 的语言开发的应用。 GraalVM 消除了编程语言之间的隔离性,并且通过共享运行时增强了他们的互操作性。它可以独立运行,也可以运行在 OpenJDK,Node.js,Oracle,MySQL 等环境中。
Graal VM(Unique VM, Polyglot VM)在HotSopt虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台 (支持Java、Scala、Groovy、Kotlin等基于Java虚拟机的语言,C、C++、Rust等基于LLVM的语言,JavaScript、Ruby、Python、R等语言),Graal VM 可以无额外开销的混合使用它们。
其基本工作原理是将语言源代码 / 编译后的中间格式转化为能被Graal VM 接受的中介格式,这个过程称为 程序特化 (Specialized, Partial Evaluation)。它提供Truffle工具集以便快速构建Sulong高性能LLVM字节码解释器。
二、内存概念模型
内存概念模型,用以表示 运行时数据区域(Runtime Data Areas)
1、程序计数器
程序计数器 (Program Counter Register) 是一块很小的内存空间,它可以看作当前线程执行字节码的行号指示器,它控制分支
、循环
、跳转
、异常处理
、线程恢复
。
2、Java 虚拟机栈
本地方法栈 (Native Method Stacks) 为虚拟机执行 Java方法 (字节码) 服务。它是线程私有的,生命周期与线程相同。
每个方法被执行时,Java虚拟机都会同步创建一个栈帧 (Stack Frame)
用于存储局部变量表、操作数栈、动态连接、方法出口等信息。执行从始至终,栈帧入栈出栈。
局部变量表存放了基本数据类型、对象引用类型,它不等同于对象本身,而是一个指向对象起始地址的引用指针。
当线程请求栈深度大于允许深度时,抛出StackOverFlowError
异常;栈扩展失败时,抛出OutOfMemoryError
异常。
3、本地方法栈
本地方法栈 (Native Method Stacks) 为虚拟机使用本地 (Native)方法服务。在如Hot-Spot等虚拟机中,将 虚拟机栈 与 本地方法栈 合二为一。
当本地方法栈深度溢出时,抛出StackOverFlowError
异常;栈扩展失败时,抛出OutOfMemoryError
异常。
4、Java 堆
Java堆 (Java Heap) 是虚拟机管理最大的一块内存,它被各线程所共享,在虚拟机启动时创建。Java 几乎所有的对象实例、数组都分配在堆空间。
Java堆是垃圾收集器管理的内存区域,也被称为GC堆。当堆无法再申请到内存时会抛出OutOfMemoryError
异常。
5、方法区
方法区 (Method Area) 与Java 堆一样,被各个线程所共享,用以存储类型信息
、变量
、静态变量
、即时编译的代码缓存
等。它逻辑上属于堆,但实际上为了与堆空间作用不同,为了区分它们,方法区还有一个别名——非堆 (Non-Heap)。
当方法区无法再申请到内存时会抛出OutOfMemoryError
异常。
6、运行时常量池
运行时常量池 (Runtime Constant Pool) 是方法区的一部分。Class文件中有类
的版本
、字段
、方法
、接口
,还有 常量池表 (Constant Pool Table) 以存放编译期间生成的各种字面量、符号引用,在类加载后会放入方法去的方法区的运行时常量池中。
当常量池无法再申请到内存时会抛出OutOfMemoryError
异常。
7、直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,但是这部分内存被频繁使用。这部分区域也可能抛出OutOfMemoryError
异常。
在JDK1.4中新加入了 NIO (New Input/Output)类,引入了基于通道 (Channel)、缓冲区 (Buffer) 的 I/O 方式。它通过使用 Native 函数库直接分配堆外内存,通过一个存储在Java堆里的DirectByteBuffer对象对这块内存引用进行操作,可以提高性能。
三、类加载机制
1、类加载的时机
- 隐式加载 new 创建类的实例,
- 显式加载:loaderClass,forName等
- 访问类的静态变量,或者为静态变量赋值
- 调用类的静态方法
- 使用反射方式创建某个类或者接口对象的Class对象。
- 初始化某个类的子类
- 直接使用
java
命令来运行某个主类
2、类的生命周期
加载:
加载字节码文件(.class)
链接:
确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。为类的静态变量分配内存并设为jvm默认的初值,对于非静态的变量,则不会为它们分配内存。将常量池中的符号引用转换为直接引用。
初始化:
执行类中定义的 Java 程序代码,此阶段是执行 () 方法的过程
使用:
使用阶段包括主动引用和被动引用,主动饮用会引起类的初始化,而被动引用不会引起类的初始化。
卸载:
满足一定条件后,JVM卸载类(在方法区中清空类信息),生命周期结束。
3、双亲委托机制
双亲委派模式:要求除顶层的启动类加载器(bootstrap),其余的类加载器都应该有自己的父类加载器,但是在双亲委派模式中父子关系采取的并不是继承的关系,而是采用组合关系来复用父类加载器的相关代码。
4、三大加载器
bootstrap
- 加载 jre/lib/下面的核心类库
extension
- 加载jre/lib/ext下的类库
application
- 加载用户类 classpath变量定义的目录