JVM 总体概述,java 高级编程内容
JVM
是Java Virtual Machine
(Java 虚拟机)的缩写,JVM
是一种用于计算设备的规范,它是一个虚构的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
JVM 屏蔽了与具体操作系统平台相关的信息,使Java
程序只需生成在Java
虚拟机上一次编译,多次运行,具有跨平台性。JVM
在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
Java
虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法区。
本文将简述以下内容:
正文
==
JVM 是什么
JDK、JRE 和 JVM 对比
JVM
,JRE
,JDK
都是 java
语言的支柱,他们分工协作。但不同的是 Jdk
和 JRE
是真实存在的,而 JVM
是一个抽象的概念,并不真实存在。
JDK
JDK
(Java Development Kit) 是 Java
语言的软件开发工具包(SDK
)。JDK
物理存在,是 programming tools
、JRE
和 JVM
的一个集合。
JRE
JRE
(Java Runtime Environment)Java
运行时环境,JRE
是物理存在的,主要由Java API
和 JVM
组成,提供了用于执行 java
应用程序最低要求的环境。
JVM
JVM
是一种用于计算设备的规范,它是一个虚构的计算机的软件实现,简单的说,JVM
是运行byte code
字节码程序的一个容器。
JVM 的特点
基于堆栈的虚拟机:最流行的计算机体系结构,如英特尔
X86
架构和ARM
架构上运行基于 寄存器。比如,安卓的Davilk
虚拟机就是基于 寄存器 结构,而JVM
是基于栈结构的。符号引用 :除了基本类型以外的数据 (类和接口) 都是通过符号来引用,而不是通过显式地使用内存地址来引用。
垃圾收集 :一个类的实例是由用户程序创建和垃圾回收自动销毁。
网络字节顺序 :
Java class
文件用网络字节码顺序来进行存储,保证了小端的Intel x86
架构和大端的RISC
系列的架构之间的无关性。
JVM 字节码
JVM
使用 Java 字节码的方式,作为Java
用户语言 和 机器语言 之间的中间语言。实现一个通用的、 机器无关 的执行平台。
JVM 能干什么
基于安全方面考虑,JVM
要求在 class
文件中使用强制性的语法和约束,但任意一门语言都可以转换为被 JVM
接受的有效的 class
文件。作为一个通用的、机器无关的执行平台,任何其他语言的实现者都可将 JVM
当作他的语言产品交付媒介。
JVM
中执行过程如下:
加载代码
验证代码
执行代码
提供运行环境
JVM
生命周期
启动:任何一个拥有
main
方法的class
都可以作为JVM
实例运行的起点。运行:
main
函数为起点,程序中的其他线程均有它启动,包括daemon
守护线程和non-daemon
普通线程。daemon
是JVM
自己使用的线程比如GC
线程,main
方法的初始线程是non-daemon
。消亡:所有线程终止时,
JVM
实例结束生命。
JVM
组成架构
JAVA
代码执行过程如下:
1. 类加载器(Class Loader)
类加载器?负责加载程序中的类型(类和接口),并赋予唯一的名字予以标识。
JDK
?默认提供的三种?ClassLoader
如下:
类加载器的关系
Bootstrap Classloader
是在Java
虚拟机启动后初始化的。Bootstrap Classloader
负责加载ExtClassLoader
,并且将ExtClassLoader
的父加载器设置为Bootstrap Classloader
Bootstrap Classloader
加载完ExtClassLoader
后,就会加载AppClassLoader
,并且将AppClassLoader
的父加载器指定为ExtClassLoader
。
类加载器的作用
| Class Loader | 实现 | 负责加载 |
| --- | --- | --- |
| Bootstrap Loader | C++ | %JAVA_HOME%/jre/lib
, %JAVA_HOME%/jre/classes
以及-Xbootclasspath 参数指定的路径以及中的类 |
| Extension ClassLoader | Java | %JAVA_HOME%/jre/lib/ext
,路径下的所有classes
目录以及java.ext.dirs
系统变量指定的路径中类库 |
| Application ClassLoader | Java | Classpath
所指定的位置的类或者是jar
文档,它也是Java
程序默认的类加载器
|
双亲委托机制
Java
中ClassLoader
的加载采用了双亲委托机制,采用双亲委托机制加载类的时候采用如下的几个步骤:
当前
ClassLoader
首先从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。当前
ClassLoader
的缓存中没有找到被加载的类的时候,委托父类加载器去加载,父类加载器采用同样的策略,首先查看自己的缓存,然后委托父类的父类去加载,一直到Bootstrap ClassLoader
。当所有的父类加载器都没有加载的时候,再由当前的类加载器加载,并将其放入它自己的缓存中,以便下次有加载请求的时候直接返回。
小结 :双亲委托机制的核心思想分为两个步骤。其一,自底向上检查类是否已经加载;其二,自顶向下尝试加载类。
ClassLoader
隔离问题
每个类装载器都有一个自己的命名空间用来保存已装载的类。当一个类装载器装载一个类时,它会通过保存在命名空间里的类全局限定名(Fully Qualified Class Name
)进行搜索来检测这个类是否已经被加载了。
JVM
及 Dalvik
对类唯一的识别是 ClassLoader id
+ PackageName
+ ClassName
,所以一个运行程序中是有可能存在两个包名和类名完全一致的类的。并且如果这两个”类”不是由一个 ClassLoader
加载,是无法将一个类的示例强转为另外一个类的,这就是 ClassLoader
隔离。
双亲委托 是 ClassLoader
类一致问题的一种解决方案,也是 Android
差价化开发和热修复的基础。
类装载器特点
Java
提供了动态加载特性。在运行时的第一次引用到一个class
的时候会对它进行装载(Loading) 、** 链接(Linking)** 和 ** 初始化(Initialization) ** ,而不是在编译时进行。不同的 JVM 的实现不同,本文所描述的内容均只限于Hotspot JVM
。
JVM
的类装载器负责动态装载,Java
的类装载器有如下几个特点:
层级结构:Java 里的类装载器被组织成了有父子关系的层级结构。Bootstrap 类装载器是所有装载器的父亲。
代理模式: 基于层级结构,类的代理可以在装载器之间进行代理。当装载器装载一个类时,首先会检查它在父装载器中是否进行了装载。如果上层装载器已经装载了这个类,这个类会被直接使用。反之,类装载器会请求装载这个类
可见性限制:一个子装载器可以查找父装载器中的类,但是一个父装载器不能查找子装载器里的类。
不允许卸载:类装载器可以装载一个类但是不可以卸载它,不过可以删除当前的类装载器,然后创建一个新的类装载器装载。
评论