Java—JVM

简介: Java—JVM

JVM


JDK / JRE / JVM 的关系

JDK 开发工具包

(java development kit) 支持开发和运行 Java 程序。

JDK 包含 JRE 以及各种 Java 开发工具(如编译器 javac 、调试器 jdb 等)。

JRE 运行环境

(java runtime environment) 能够运行已编译的 Java 程序。

JRE 包含 JVM 以及运行时所需调用的基础类库(如 java.lang 包、 java.util 包等)。

JVM 虚拟机

(java virtual machine) 运行 Java 程序的工作环境。

Java 程序在虚拟机上运行而不是直接在操作系统上运行,从软件层面屏蔽了底层硬件指令的细节。虚拟机会根据操作系统自动将字节码文件转化成相应的机器码,使 Java 字节码文件能够在多种平台上不加修改地运行。

HotSpot 虚拟机 是 SunJDK 和 OpenJDK 中所带的虚拟机,也是目前使用范围最广的 Java 虚拟机。


Java 运行原理

基本概念

解释和编译

  • 解释:源文件经过编译器编译成为脚本文件,由解释器逐行解释并执行。灵活性更好。
  • 编译:源文件经过编译器编译成为可执行文件,由计算机直接去执行。性能更好。

静态编译和动态编译

  • 静态编译:编译时确定类型,绑定对象。性能更好。
  • 动态编译:运行时确定类型,绑定对象。能更好地支持多态,灵活性强。

Java 运行原理

Java 是编译和解释并行的语言,采取动态编译,支持反射机制。

  1. 源文件(.java) 经过编译器编译成为 字节码文件(.class) ,通过类加载器搬运到 JVM 中逐行解释并执行。
  2. 即时编译器(JIT) 在编译时会识别反复执行的热点代码(超过 10000 次)并保存机器码,复用时可直接由 JVM 执行。

优势:以虚拟机作为中介,字节码文件可以在所有操作平台上通用。即一次编译、到处运行。

劣势:但这也导致了 Java 语言性能不如 C/C++ 等编译语言。


类加载

在 Java 程序里如果使用某个尚未加载到内存中的类,JVM 会通过加载、链接、初始化 3 个步骤来对该类进行初始化。

!类

创建类的实例,访问类的静态变量,或者调用类的静态方法都会导致类的初始化。但要注意对于 final 常量,如果在编译时就可以确定该变量的值,编译器会在编译时直接把这个变量替换成它的值,因此即使程序使用该静态变量,也不会导致该类的初始化。

加载

类的加载由类加载器完成。类加载器会读取类的字节码文件,并为之创建一个 java.lang.Class 对象。

类加载器使用双亲委派模型,类加载器具备一种带有优先级的层次关系:

  • 根类加载器:负责加载 Java 的核心类,原生代码实现,并不继承自 java.lang.ClassLoader 类。
  • 扩展类加载器:负责加载 JRE 扩展目录中的类。由 Java 语言实现,没有父类加载器。
  • 系统类加载器:负责加载 CLASSPATH 路径中的类。由 Java 语言实现,父类加载器为扩展类加载器。
  • 用户类加载器:开发者通过继承 ClassLoader 基类来创建的类加载器。由 Java 语言实现,默认父类加载器为系统类加载器。

任何类加载器在接到加载类的请求时,都会将加载任务委托给父类加载器,最终委派给处于模型最顶端的根类加载器进行加载。只有父类加载器无法完成此加载任务时,才自己去加载。如果均无法载入类,则抛出 ClassNotFountException 异常。

  1. 通过这种层级关系可以避免类的重复加载。
  2. 其次可以防止核心 API 库被随意篡改,用户即使编写了 java.lang.Object 的同名类,也永远无法被加载运行。

【在双亲委派模型中,由父加载类加载的类,下层加载器是不能加载的。用户在 classpath 路径下自定义的java.*包内的类会抛出异常:SecurityException: Prohibited package name】

链接

当类被加载并生成 Class 对象后,连接阶段负责把类的二进制数据合并到 JRE 中。

  • 验证:验证阶段用于检验被加载的类是否有正确的内部结构,并和其他类协调一致。
  • 准备:类准备阶段负责为类的静态变量分配内存,并赋予类型的默认值。
  • 解析:将类的二进制数据中的符号引用替换成直接引用,指向具体的内存空间。

Java 是相对 C++ 语言是安全的语言,验证过程用于确保 Class 文件的字节流符合当前虚拟机要求,不会危害虚拟机自身安全。其主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。

初始化

为类的静态变量赋予程序设定的初始值。



目录
相关文章
|
3月前
|
存储 算法 Java
惊!Java程序员必看:JVM调优揭秘,堆溢出、栈溢出如何巧妙化解?
【8月更文挑战第29天】在Java领域,JVM是代码运行的基础,但需适当调优以发挥最佳性能。本文探讨了JVM中常见的堆溢出和栈溢出问题及其解决方法。堆溢出发生在堆空间不足时,可通过增加堆空间、优化代码及释放对象解决;栈溢出则因递归调用过深或线程过多引起,调整栈大小、优化算法和使用线程池可有效应对。通过合理配置和调优JVM,可确保Java应用稳定高效运行。
140 4
|
1月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
63 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
1月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
31 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
25天前
|
存储 算法 Java
Java虚拟机(JVM)的内存管理与性能优化
本文深入探讨了Java虚拟机(JVM)的内存管理机制,包括堆、栈、方法区等关键区域的功能与作用。通过分析垃圾回收算法和调优策略,旨在帮助开发者理解如何有效提升Java应用的性能。文章采用通俗易懂的语言,结合具体实例,使读者能够轻松掌握复杂的内存管理概念,并应用于实际开发中。
|
1月前
|
存储 算法 Java
深入理解Java虚拟机(JVM)及其优化策略
【10月更文挑战第10天】深入理解Java虚拟机(JVM)及其优化策略
41 1
|
1月前
|
安全 Java API
🌟探索Java宇宙:深入理解Java技术体系与JVM的奥秘
本文深入探讨了Java技术体系的全貌,从Java语言的概述到其优点,再到Java技术体系的构成,以及JVM的角色。旨在帮助Java开发者全面了解Java生态,提升对Java技术的认知,从而在编程实践中更好地发挥Java的优势。关键词:Java, JVM, 技术体系, 编程语言, 跨平台, 内存管理。
35 2
|
1月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
41 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
1月前
|
监控 Java
Java的JVM如何优化?
Java的JVM如何优化?
56 3
|
2月前
|
存储 缓存 监控
【Java面试题汇总】JVM篇(2023版)
JVM内存模型、双亲委派模型、类加载机制、内存溢出、垃圾回收机制、内存泄漏、垃圾回收流程、垃圾回收器、G1、CMS、JVM调优
【Java面试题汇总】JVM篇(2023版)
|
2月前
|
安全 前端开发 Java
浅析JVM invokedynamic指令与Java Lambda语法的深度融合
在Java的演进历程中,Lambda表达式无疑是Java 8引入的一项革命性特性,它极大地简化了函数式编程在Java中的应用,使得代码更加简洁、易于阅读和维护。而这一切的背后,JVM的invokedynamic指令功不可没。本文将深入探讨invokedynamic指令的工作原理及其与Java Lambda语法的紧密联系,带您领略这一技术背后的奥秘。
29 1