当前位置: 首页 > article >正文

八股文-JVM

是什么?有什么用?谁发明的?什么时候发明的?

Java虚拟机,用来运行Java程序,有很多个版本的虚拟机,比如HotSpot,最开始是SUN公司开发人员,和Java一起发布,现在被Oracle收购了

计算机不能直接运行吗?为啥要中间多一层虚拟机?

计算机可以执行机器代码,很多编程语言也是编译成机器码,j交给计算机直接运行的,不过Java为了实现一次编写代码,可以跨平台执行,所以加了一层虚拟机,同时虚拟机也简化了我们的编码,比如自动内存管理、垃圾回收、编译优化(解释执行/即时编译)

特性

跨平台、安全、内存管理、性能优化(即时编译-JIT)、多线程支持

运⾏时数据区

方法区、堆、栈(Java方法栈、本地方法栈)、程序计数器、
在这里插入图片描述

类加载过程

类加载是指,把编译好的class文件加载到运行时数据区,需要经过以下过程:
1、加载:加载是指查找字节流,并且据此创建类的过程。加载需要借助类加载器,在 Java 虚拟机中,类加载器使用了双亲委派模型,即接收到加载请求时,会先将请求转发给父类加载器。
2、链接:是指将创建成的类合并至 Java 虚拟机中,使之能够执行的过程。链接还分验证、准备和解析三个阶段。其中,解析阶段为非必须的。分配内存、符号引用解析成为实际引用
3、初始化:类加载的最后一步是初始化,便是为标记为常量值的字段赋值,执行 < clinit > 方法,会加锁保证类的初始化仅会被执行一次,这个特性被用来实现单例的延迟初始化。

类加载器和双亲委派机制

启动类加载器(C++实现):启动类加载器负责加载最为基础、最为重要的类,比如存放在 JRE 的 lib 目录下 jar 包中的类(以及由虚拟机参数 -Xbootclasspath 指定的类)。

其他类加载器:都是 java.lang.ClassLoader 的子类,这些类加载器需要先由另一个类加载器,比如说启动类加载器,加载至 Java 虚拟机中,方能执行类加载。双亲委派模型。每当一个类加载器接收到加载请求时,它会先将请求转发给父类加载器。在父类加载器没有找到所请求的类的情况下,该类加载器才会尝试去加载。
扩展类加载器(extension class loader)和应用类加载器(application class loader),均由 Java 核心类库提供。

扩展类加载器的父类加载器是启动类加载器。它负责加载相对次要、但又通用的类,比如存放在 JRE 的 lib/ext 目录下 jar 包中的类(以及由系统变量 java.ext.dirs 指定的类)。

应用类加载器的父类加载器则是扩展类加载器。它负责加载应用程序路径下的类。(这里的应用程序路径,便是指虚拟机参数 -cp/-classpath、系统变量 java.class.path 或环境变量 CLASSPATH 所指定的路径。)默认情况下,应用程序中包含的类便是由应用类加载器加载的。
Java 9有略微调整。
自定义的类加载器:自己实现的类加载器,可以满足一些自定义需求,比如对代码进行加解密

为啥要有双亲委派机制

保证一个类只被加载一次:因为父类加载了,子类就无需加载了,这样只会被加载一次
保证核心类库的安全性:防止子加载器去篡改核心类库的加载,比如写给自定义加载器把一些工具类给改了
解耦:类似与网络分层,各自做好自己的职责就行

解释执行/即时编译

解释执行:即逐条将字节码翻译成机器码并执行;
即时编译(JIT):即将一个方法中包含的所有字节码编译成机器码后再执行。
前者的优势在于无需等待编译,而后者的优势在于实际运行速度更快。HotSpot 默认采用混合模式,综合了解释执行和即时编译两者的优点。

理论上讲,即时编译后的 Java 程序的执行效率,是可能超过 C++ 程序的。这是因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。
在这里插入图片描述

JRE、JDK、JVM关系?

JVM:Java虚拟机,是一个可以执行 Java 字节码的虚拟计算机。
JRE:Java 运行时环境,是运行 Java 应用程序所需的最小环境。包括了 Java 虚拟机(JVM)、核心类库和支持文件。
JDK: Java 开发工具包,是用于开发 Java 应用程序的工具集合,包括了 Java 运行时环境(JRE)、Java 编译器(javac)、Java 虚拟机(JVM)和各种工具(如 javadoc、javap 等)。

关系
JDK 包含了 JRE 和开发工具。
JRE 包含了 JVM 和运行 Java 程序所需的类库。
总结
如果你需要开发 Java 程序,需要安装 JDK。
如果你只需要运行 Java 程序,安装 JRE 就足够了。
JVM 是 JRE 的核心部分,负责执行 Java 程序。

垃圾回收

内存有限,对运⾏时数据区中的数据进⾏管理和回收。垃圾收集器分为串行、并行、并发。回收机制不同,吞吐量和响应时间不同。比如回收时间,各个阶段采用单线程还是多线程
Serial、Parallel、CMS、G1、ZGC。核⼼的算法有 3 个:标记-清除、标记- 整理、复制

JVM调优

主要也是围绕吞吐量和响应时间,对应响应时间有要求的,那就尽量减少STW时间,反正就是通过一下工具看运行时,YGC和FGC次数,以及每次STW时间,通过不断调整和观测来设置一个合理的值
实际工作中一方面是调整JVM参数,另一方面是修改代码。
参数问题可能有以下一些情况:
1、堆内存设置太小,不满足程序的正常运行,比如一个线程调用一个方法可以会占用1M,内存,并发数为1000,堆内存设置500M的话,一下就满了,这个时候可能会出现频繁垃圾回收或者OOM
2、堆内存新生代和老年代比例不合理,大部分都是临时对象的程序,新生代可以调大。
3、还有一些参数可以指定STW最大时间,这时JVM会根据实际情况调整部分内存大小
4、更换垃圾回收器,参考:https://blog.csdn.net/2301_79437276/article/details/140291840

一次GC流程

堆分为新生代和老年代,新生代分为E区和S区(S0、S1)。
新对象放入E区,大对象也可能直接放入老年代,E区满了会进行Young GC,存活下来的对象放入S区,S区里面存活下来的对象会进行一次负责,在S0和S1之间每复制一次,对象存活的分代年龄+1,当达到15时放入老年代,老年代满了会进行Full GC

为啥是15呢?

因为分代年龄存储在对象头里面,采用4bit存储,最大值只支持15

参考

https://www.cnblogs.com/tomakemyself/p/14147989.html


http://www.kler.cn/a/311324.html

相关文章:

  • 【C#设计模式(8)——过滤器模式(Adapter Pattern)】
  • 灰狼优化算法
  • 【日志】392.判断子序列
  • 使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件
  • STM32 GPIO 配置
  • LLMs之Code:Github Spark的简介、安装和使用方法、案例应用之详细攻略
  • 黑马程序员Java笔记整理(day01)
  • 用idea编写并运行第一个spark scala处理程序
  • RK3568平台(网络篇)MAC地址烧录
  • 工业仪器仪表指针数据集
  • 基于Python实现的一个电影知识库QA系统
  • 网络安全 DVWA通关指南 DVWA Stored Cross Site Scripting (存储型 XSS)
  • 卷积和转置卷积的输出尺寸计算
  • Qt_显示类控件
  • 二叉树的层序遍历-广度优先遍历
  • 专题四_位运算( >> , << , , | , ^ )_算法详细总结
  • 图新地球-将地图上大量的地标点批量输出坐标到csv文件【kml转excel】
  • 汇编(实现C语言程序的调用)
  • TestDeploy v3.0构思
  • Vue2接入高德地图API实现搜索定位和点击获取经纬度及地址功能
  • 【Python报错已解决】ModuleNotFoundError: No module named ‘sklearn‘
  • 离散化c++
  • Django创建模型
  • 力扣(leetcode)每日一题 1184 公交站间的距离
  • 机器人相关知识的本身和价值
  • C++实现的小游戏