- JVM、JDK、JRK的概念
- 什么是JVM?
- JVM能够跨平台的原因
- 运行时数据区
- 堆区
- 方法区
- 栈区
- JVM如何确定垃圾
- 垃圾回收算法
- JVM的类加载阶段
- 类加载器
- 双亲委派模型
JDK是Java Development Kit,它是功能齐全的JavaSDK,它拥有JRE所拥有的一切,还有编译器(javac)和工具(javadoc、jdb),能够创建和编译程序。
JRE是Java运行时的环境,它时运行已编译好的Java程序所需所有内容的集合,包括JVM,Java类库,Java命令和其他一些基本构建,但是JRE不能独立创建新程序。
什么是JVM?JVM是运行Java字节码的虚拟机,包括一个类加载器子系统、运行时数据区、执行引擎和本地接口库。其中,类加载器子系统用于将编译好的字节码文件class加载到JVM中,运行时数据区用于存储JVM运行时产生的数据,包括方法区,堆区,本地方法栈,虚拟机栈和程序计数器;执行引擎包括了即时编译器和垃圾回收器,即时编译器用于将字节码文件class编译成机器码,而垃圾回收器用于回收JVM运行时不再使用的对象;本地接口库用于调用本地方法库与操作系统交互,完成具体的指令操作。
Java源文件在通过编译器后被编译成相应的字节码文件class,字节码又被JVM中的解释器编译成机器码,然后运行在不同的操作系统上,每种操作系统的解释器都不同,但是基于解释器实现的虚拟机是相同的,可以理解为,在运行时编译器线程将字节码文件动态编译成本地平台机器码。
运行时数据区 堆区用于存放JVM运行时创建的对象。由于现代JVM采用分代收集算法,因此Java堆从GC的角度还可以细分为新生代、老年代和永久代。
方法区用于存储常量、静态变量、类信息(类的版本,字段,方法,接口表述)、即时编译器编译后的机器码、运行时常量池等数据。
栈区用于存放基本数据类型的数据、对象的引用和临时变量。
用new创建的对象在堆区,函数中的临时变量在栈区,Java中的字符串在方法区。Java方法执行内存模型,用于存储局部变量、操作数栈、动态链接、方法出口等信息,是线程隔离的。
JVM如何确定垃圾Java采用引用计数法和可达性分析来确定对象是否应该被回收。其中,引用计数法容易产生循坏引用的问题,可达性分析通过根搜索算法来实现,其中根搜索算法以一系列GC Roots的点作为起点向下搜索,在一个对象到任何GC Roots都没有引用链时,说明其已经死亡了。根搜索算法主要是针对栈区中的引用,方法区的静态引用和JNI中的引用展开分析。
垃圾回收算法- 引用计数算法: 对引用的对象部署计时器
- 复制算法:对Eden中的幸存to、from区进行来回复制;
- 标记清除算法:扫描对象并对使用的对象进行标记,对没有标记的对象进行清除。好处:不需要额外的空间;坏处:两次扫描浪费时间且会产生内存碎片。
- 标记压缩算法:对标记的对象向前存在的空位移动,减少内存碎片。好处:不存在内存碎片;坏处:增加移动成本。
GC采用分代收集算法,即对新生区采用复制算法,对老年区采用标记清除和标记压缩算法混合实现。
JVM的类加载阶段分为5个阶段:加载、验证、准备、解析、初始化、使用、卸载
- 加载: 指JVM读取字节码文件,并且根据字节码文件描述创建对象的过程。类加载过程主要包括将字节码文件读取到运行时数据区的方法区内,在堆中创建对象,并封装类在方法区的数据结构过程。
- 验证:主要用于包装字节码文件符合当前虚拟机的要求,保障虚拟机自身安全,只有通过验证的字节码文件才能被JVM加载。
- 准备:主要工作是在方法区中为类变量分配空间并设置类中变量的初始值。
- 解析:JVM会将常量池中的符号引用替换成直接引用。
- 初始化:主要是为类进行初始化。
- 启动类加载器: 负责加载JAVA_HOME/lib目录中或-Xbootclasspath参数指定的路径中被虚拟机认可的类
- 扩展类加载器:负责加载JAVA_HOME/lib/ext目录中或java.ext.dirs系统变量指定路径的类
- 应用程序类加载器:负责加载用户路径classpath上的类库
- 自定义类加载器:继承java.lang.classLoader实现的
如何一个类受到类加载请求,自己不会去加载这个类,而是将类加载请求委派给父类加载器,这样一层层向上传递,直到启动类加载器。只有当父类加载器无法加载时,子加载器才会尝试自己去加载。
作用: 保障类的唯一性和安全性。