JVM知识点(一)
1、JVM基础概念
(1)JVM、JRE、JDK
- JRE:JVM+基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译,处处运行的,需要有一个基本类库告诉JVM如何操作运行,如如何操作文件,连接网络等,JVM运行时,会一次性加载基本类库;
- JDK:JDK中除了包含JRE,同时还包含一些小工具,如javac,jar等;如果只要运行java程序,只需JRE即可
- 三者之间关系:JDK>JRE>JVM
(2)java程序运行
- 编写好一个java程序后,使用javac进行编译后,会生成字节码文件(java程序中,使用的输出等模块是JRE里提供的基本类);
- JVM采用栈架构,其指令由操作码(opcode)+操作数组成,操作码只有1个字节长度(0-255),指令数不能超过256条
- 我们运行.class文件时,会启动一个JVM进程,JVM通过opcode+操作数来执行程序;
- 执行方式有:解释执行:将opcode+操作数翻译为机器码文件;JIT:及时编译,在特定条件下,会将字节码编译为机器码文件之后才执行;
2、JVM内存管理
(1)JVM内存布局
- 堆中的数据是共享的,占用内存最大;
- 执行字节码文件的模块为执行引擎,线程切换通过程序计数器进行;
(2)虚拟机栈
- 虚拟机栈是基于线程的,main方法启动后,以线程方式运行;
- 线程的生命周期和栈的生命周期一样,栈中每一条数据都是栈帧;
- 每调用一个java方法,就会创建一个栈帧并入栈,执行完改该方法后,便会出栈;
- 所有栈帧出栈后,线程就会结束;
(3)程序计数器
- 程序计数器可以看作当前线程执行字节码的行号指示器,存储的是当前线程的进度;
- 程序计数器有线程创建时产生,配合虚拟机站完成计算操作;
(4)堆
- 申请的对象都是存储在堆中,垃圾回收的对象是堆;
- 堆在程序启动时创建,随着对象不断创建,堆空间会越来越小,就需要对堆内不常用的对象进行回收,即GC;
- java对象分为基本数据类型和普通对象,基本数据类型存储在栈中;对于普通对象,JVM会在堆中创建对象,其他地方使用该应用;
- 堆中的数据是线程共享的,栈中的数据是线程私有的;
(5)元空间
- Java8之前,类(创建对象的模板)都是存储在永久代中,且不会被JVM回收,随着类的增多,会造成JVM内存溢出;
- java8中,元空间替代了永久代,在非堆上存储,JVM不会再出现方法区的内存溢出,但无限使用元空间会造成操作系统挂掉,可以通过-XX:MaxMetaspaceSize控制元空间大小;
3、类加载
(1)类加载过程
- 加载:将.class文件加载到JVM的方法区中;
- 验证:判断.class是否可以加载进入JVM中,不合法的直接抛出异常;低版本的JVM无法加载高版本类库;
- 准备:为类变量分配内存,并初始化值;此时实例对象还未分配内存,所以此时是在方法区中进行的;如下图,类变量有两次赋值,一次在准备阶段(赋予默认值),一次在初始化阶段(赋予程序员指定的值),而局部变量不会初始化赋值,没有默认值,不指定会报错;
- 解析:将符号应用替换为直接应用。符号应用就是我们定义的字面量,如int a=1;a就是符号应用;直接应用是直接执行目标对象的指针或偏移量;
- 初始化:初始化成员变量;类信息只会存储一份到方法区中,类加载只会加载一次,所以static变量和staic代码块只会加载一次;JVM保证子类初始化之前,父类已经初始化;
如下代码,第一次new时执行顺序 :父类A的静态方法块---子类B的静态方法块--父类A的构造方法--子类B的构造方法;第二new时执行顺序 :父类A的构造方法--子类B的构造方法
public class A {static {System.out.println("A static 代码块");//只会执行一次}public A(){System.out.println("A 构造方法");}
}class B extends A{static {System.out.println("B static 代码块");//只会执行一次}public B(){System.out.println("B 构造方法");}public static void main(String[] args) {A a = new B();//第一次会调用静态代码块B b = new B();//不会调用静态代码块}
}
(2)类加载器
- 双亲委派机制:除了顶层的启动类加载器外,其余加载器都会在加载类时,先委托给父类加载,父类无法加载,才会交给子类去加载;
- 双亲委派机制可以避免,我们自定义的类去覆盖java类库,保证java程序的安全;如下,我们使用写一个java.lang.String,运行main方法时,会报错,因为加载String类时是由父类加载器加载,加载的是java类库中的String,而java类库中的String类没有main方法,所以会报错;
- Tomcat中会破坏双亲委派机制,每一个web应用都有一个WebappClassLoader加载器,会加载应用程序类,只有当自己加载不到,才会委托给父类;使用该类加载器,JVM中出现同一个第三方库不同版本,应用程序去使用他不会发生冲突,他们由各自的类加载器加载,相互隔离(类对象是否相同由类加载器的命名空间和类对象本身决定,不同类加载器,命名空间不同);
4、JVM异常处理
(1)异常表
- 每一个方法都会携带一个异常表,表中每一行代表一个异常处理器,并且由from指针、to指针、target指针和异常处理类型组成;这些指针指向的是字节码的索引
- from指针到to指针表示异常监控的范围(try中代码),target指针表示异常起始处理器的开始位置,即catch代码,如下图:异常监控范围为0-7(不包括7),异常处理器开始范围为10;
(2)异常处理
- 程序出现异常时,JVM会开始从上到下遍历异常表中每一行,当出现异常的字节码索引在异常表中的某一行的监控范围内,则会判断监控表的异常类型是否和当前程序抛出的异常类型一致,如果一致,会将字节码索引转到target指针;
- 如果遍历完异常表的每一行,还是没有找到可以处理异常的异常处理器,则会将该方法的栈帧出栈,继续遍历调用该方法的调用者的异常表,重复异常表查找操作,最坏会遍历当前栈上所有方法的异常表;
(3)finally块
- finally代码块的内容会赋值到try和catch块中;
- 异常表中会生成一行异常处理条目,监控整个try-catch,并且捕获异常;
- 在执行完finally块后,若有捕获异常则会抛出异常;
5、OOM
(1)GC Root
- 发生GC回收时,对于每一个对象,JVM总是回去寻找引用他的祖先,如果这个引用祖先已经挂了,就会将该对象回收,能够躲避垃圾回收的祖先,就是GC root;
- 从GC root向下搜索,会产生一条Refrence Chain链,当任何一个对象不能和任何一个GC root产生关系时,就会被回收;
- GC过程是找出活的对象,并认定其他对象为”无用”;
GC Root有:
- java线程中,当前正在被调用的方法的引用类型参数、局部变量、临时值等,即和栈帧相关的各种引用;
- 所有被加载的java类;
- java类的引用类型的静态变量;
- 运行时常量池里的引用类型;(String或Class类型)
- 用于同步的监控对象,如调用了对象的wait()方法;
大致分为三类:
- 线程中的相关引用;
- 类静态变量的引用;
- JNI引用;
(2)引用类型
- 强引用:当内存不足的时候,JVM会抛出OutOfMemoryError错误,即使程序终止,该对象也不会被回收;只有将GC Root与其断开,该对象才会被回收;如:我们平常new一个对象即为一个强引用,大量对象被创建且不能被回收时,会造成内存泄漏;
- 软引用:维护一些可有可无的对象,只有当堆内存空间不够时,才会回收该对象,如果回收了软引用后,JVM内存空间任然不足,则会抛出OutOfMemoryError;软引用可以和引用队列(ReferenceQueue)联合使用,当软引用所引用的对象被回收后,就会加入到该队列中;
- 弱引用:GC回收时,不管空间是否足够,都会回收弱引用所指向的对象;与WeakRefenece联合使用;
- 虚引用:采用该引用的对象,在GC回收时,都会被回收;虚引用必须和引用队列联合使用。当GC回收对象时,若该对象有虚引用,则会将虚引用加入到引用队列中,在对象被回收之前,会执行一些逻辑;
(3)OOM原因
- OOM可能发生的区域有堆、本地方法栈、虚拟机栈、元空间;
- 内存容量过小,需要调整堆大小;
- 错误的引用方式,发生内存泄漏。没有及时清理与GC Root相关联系,如线程池中的线程,复用时没有清理ThreadLocal,会造成内存泄漏;
- 堆接口参数没有校验,传入参数超出范围;
- 堆外内存无限制使用,会导致操作系统资源耗尽;
6、垃圾回收
(1)垃圾回收算法
- 标记清除算法:根据GC Root遍历所有可达对象,进行标记,清除掉未标记的对象,该算法会造成内存碎片,如下,当GC回收后,剩余总空间还有7K,分配一个6K空间,无法分配成功
- 标记复制算法:会预留一半的空间,每次GC回收时,会将活对象复制到空闲的空间中去,所有算法中效率最高,但会造成空间浪费
标记整理:GC回收时,会将存活对象整理到一起,不会造成空间浪费,同时也不会出现内存碎片,但效率最低;
(2)年轻代
- 存活时间比较短的对象都存储在年轻代中;
- 使用的清除算法是标记复制算法,因为年轻代中存活的对象比较少,使用标记复制算法可以大大提高效率;
- 年轻代分为伊甸园去(Eden)和两个幸存区(Survivor),对象会先分配到伊甸园区;
- 当Eden区分配满时,会触发年轻代GC(Minor GC),过程如下:
-
- Eden第一次GC,先将Eden区存活的对象移动到一个幸存区;
- Eden再次GC,会采用标记复制算法,会将清理from区和Eden区,存活的对象被移动到To区,最后将From区清空即可;
- Eden:From:To=8:1:1,会浪费10%的空间;-XX:SurvivorRatio可以设置(默认为8);
- JVM会为每一个线程分配一个缓冲区(TLAB),TLAB在Eden区中,每个线程可以在TLAB上分配对象,避免锁竞争,但如果分配对象大小超过TLAB大小,则会在Eden区的公共区域分配;
(3)老年代
- 老年代一般采用标记清除、标记整理算法(Major GC),因为对象存活比较久,空间占用率比较大,拷贝不划算;
- 对象进入老年代途径:
-
- 提升:根据对象的年龄判断是否需要进入老年代,每发生一次Minor GC,存活下来的对象年龄会加1,直到年龄超过阈值,对象就会进入老年代;若对象不可达,会等到老年代发生GC时,才会被回收;阈值,可以通过参数 ‐XX:+MaxTenuringThreshold 进行配置,最大值是 15
- 分配担保:年轻代中每次存活的对象都会进入Survivor区中,这个区域只有10%的大小,但我们无法保证每次存活对象的大小不超过10%,当Survivor空间不够时,就需要依赖老年代进行担保,直接在老年代中分配对象;
- 大对象直接分配到老年代:当对象大小超过指定大小,会直接在老年代中分配对象;通过设置该参数 -XX:PretenureSizeThreshold,默认为0(即对象都在年轻代分配);
- 动态对象年龄判断:当幸存区中相同年龄对象的大小的和超过幸存区的一半,幸存区中大于等于这个年龄的对象都会进入老年代;
(4)年轻代垃圾回收器
- Serial 垃圾收集器:处理GC只有一个线程,GC时会停止一切用户进程,通常用于客户端应用,因为客户端应用通常不会建立过多对象;
- ParNew垃圾收集器:处理GC时有多个线程,GC时同样会停止用户线程,存在线程切换开销,在单CPU下,性能比Serial差;追求降低用户停顿时间;
- Paraller Scavenge垃圾收集器:也是多线程处理,追求CPU高吞吐量,能够在较短时间内完成指定任务;
(5)老年代垃圾收集器
- Serial Old垃圾回收器:同样为单线程版本,采用标记整理算法,而年轻代中采用标记复制算法;
- Parallel Old垃圾回收器:多线程处理,追求CPU高吞吐;
- CMS垃圾回收器:以获取最短用户停顿时间为目标的垃圾收集器,采用用户线程和回收线程并发执行,用户不会感觉到明显的停顿;
7、CMS垃圾收集器
- CMS全称并发标记清除垃圾回收器,在年轻代使用标记复制算法,在老年代使用标记清除算法,会造成内存碎片,只有通过full GC时,进行整理;
- 主要是为了降低清除老年代时,避免长时停顿;
- UseCMSCompactAtFullCollection:默认开启,表示Full GC时,需要整理内存,不能和用户进程并发,会造成停顿;
回收过程如下:
- 初始标记:只标记和GC Root直接相连的对象,因为GC Root在追踪活对象时最为耗时,同时还要标记与年轻代相关的对象;会发生STW;
- 并发标记:在初始标记阶段,进行并发标记(标记所有可达对象)。该阶段最为耗时,但可以和用户进程并行。在该阶段,可能会有对象引用发生变化,引用发生变化的对象会被标记为dirty,用于后续重新扫描;
- 并发预清理:并发预清理被标记为dirty状态的对象,将dirty状态对象重新标记,清除dirty状态,该阶段和用户进程并发,因此可能还会出现dirty状态;
- 并发可取消的预清理:重新标记阶段是需要STW(停止用户线程),因此在满足某些条件时,可以终止标记,避免会扫年轻代大量对象;
- 最终标记:第二次STW,标记所有存活对象。之前的并发预清理阶段可能发生多次,可能赶不上应用变化清况,所以最终标记会STW,停止应用进程,最终标记所有存活对象;
- 并发清除:与用户进程并发进行,清除所有不可达对象,可能会产生新的垃圾(浮动垃圾),该部分垃圾只能等到下一次GC时才能被回收;
- 并发重置:重置与CMS相关的内部结构,为下一次GC做准备;
相关文章:

JVM知识点(一)
1、JVM基础概念 (1)JVM、JRE、JDK JRE:JVM基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译,处处运行的,需要有一个基本类库告诉JVM如何操作运行,如如何操作文件,连接网络等&#x…...

网页接口导入postman进行接口请求
postman版本:v10.17.4 一、拷贝接口信息 网页打开开发者工具-networkk,在网页上请求一次接口,鼠标指在接口上,点击鼠标右键-copy-copy as cURL(bash) 二、导入postman 打开postman,点击import-Raw text,…...

【Leetcode】124.二叉树中的最大路径和(Hard)
一、题目 1、题目描述 二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root ,返回其…...

django自动创建model数据
目前使用的环境:django4.2.3,python3.10 django通过一些第三方库,可以轻易的自动生成一系列的后台数据。 首先先创建一个数据库: 然后,在setting.py中就可以指定我们新创建的数据库了。 DATABASES {default: {ENGI…...
vscode 远程连接
这里记录的是修改ssh配置文件的方式远程连接服务器中的docker服务器 假如已经创建好了docker,并已经启动 1. config ssh in dev container 在/etc/ssh/sshd_config中修改 PermitRootLogin in 把 PermitRootLogin prohibit-password 修改为 PermitRootLogin yes …...

Error running ‘Tomcat 8.5.29‘ Address localhost:1099 is already in use
一、Error running ‘Tomcat 8.5.29’ Address localhost:1099 is already in use 原因:端口1099被占用了。 二、解决 2.1 解决方法一-结束该端口1099占用 //1-查看端口占用,根据端口号1099,获取PID(进程ID) netstat -ano | findstr "…...
后端面试话术集锦第 七 篇:nginx面试话术
🚗后端面试集锦目录 💖后端面试话术集锦第 1 篇:spring面试话术💖 💖后端面试话术集锦第 2 篇:spring boot面试话术💖 💖后端面试话术集锦第 3 篇:spring cloud面试话术💖 💖后端面试话术集锦第 4 篇:ElasticSearch面试话术💖 💖后端面试话术集锦第 5 …...
leetcode算法题--使子序列的和等于目标的最少操作次数
原题链接:https://leetcode.cn/problems/minimum-operations-to-form-subsequence-with-target-sum/description/ 视频讲解:https://www.bilibili.com/video/BV1Em4y1T7Bq?t1456.1 这题是真的难。。 func minOperations(nums []int, target int) int…...

服务器部署前后端项目-SQL Father为例
hello~大家好哇,好久没更新博客了。现在来更新一波hhh 现在更新一下部署上的一些东西,因为其实有很多小伙伴跟我之前一样,很多时候只是开发了,本地前后端都能调通,也能用,但是没有部署到服务器试过&#x…...

LiveNVR监控流媒体Onvif/RTSP功能-支持语音对讲支持非国标摄像头SDK语音对讲GB28181级联国标平台非国标转国标语音对讲
LiveNVR支持语音对讲支持非国标摄像头SDK语音对讲GB28181级联国标平台非国标转国标语音对讲 1、确认摄像头是否支持对讲2、摄像头视频类型复合流3、通道配置SDK接入4、视频广场点击播放5、相关问题5.1、如何配置通道获取直播流?5.2、如何GB28181级联国标平台&#x…...

爬虫selenium获取元素定位方法总结(动态获取元素)
目录 元素 查看元素信息 元素定位 通过元素id定位 通过元素name定位 通过xpath表达式定位 绝对路径 相对路径 通过完整超链接定位 通过部分链接定位 通过标签定位 通过类名进行定位 通过css选择器进行定位 id选择器 class选择器 标签选择器 属性选择器 定位带…...

JVM下篇知识
第01章:概述篇 第02章:JVM监控及诊断工具-命令行篇 第03章:JVM监控及诊断工具-GUI篇 第04章:JVM运行时参数 第05章:分析GC日志...
HBase客户端的批量写缓存BufferedMutator
HBase数据刷写 之前提到过这个方法,那么BufferedMutator是什么?又应该如何实现呢? 写缓存 HBase的每一个put操作实际上是一个RPC操作,将客户端的数据传输到服务器再返回结果,这只适用于小数据量的操作,如…...
从多个角度详解map转为list
从多个角度详解map转为list 更新:2023-05-20 19:24 在Java编程中,我们经常使用map存储键值对数据,而有时我们需要把map转为list,本文将从多个方面对map转list做详细的阐述。 一、map转为list的基础方法 Java提供了多种方法将m…...
PHP用CURL发送Content-type为application/json的POST请求方法
HELLO 各位伙伴,最近一直在做项目,没有及时更新。望请见谅。 今天,给大家讲一下php请求第三方接口的时候遇到的问题,大家都知道,在请求第三方接口的时候,会要求我们用post还是get来传参 一般我们传参的时候…...

【程序猿书籍大放送:第二期】《强化学习:原理与Python实战》
🌹欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享,与更多的人进行学习交流 爱书不爱输的程序猿:送书第二期 一、搞懂大模型的智能基因,RLHF系统设计关键问答1.RLHF是什么?2.RLHF适用于哪些任务?3…...

SV-6002Y 网络对讲求助模块,带3W功放输出和一路30W功放输出
SV-6002Y 网络对讲求助模块,带3W功放输出和一路30W功放输出 SV-6002Y是我司一款求助对讲模块,具有10/100M以太网接口,其接收网络的音频数据,实时解码播放,还可配置麦克风输入和扬声器输出。SV-6002Y可实现对讲、广播、…...

Nginx详解 二:配置文件部分
文章目录 1. Nginx 配置文件1.1 主配置文件1.2 子配置文件1.3 全局配置1.3.1 修改启动的进程数1.3.2 cpu和work进程绑定(nginx调优)1.3.3 修改PID路径1.3.4 nginx进程的优先级(work进程的优先级)1.3.5 调试work进程打开的文件的个…...

SMC_TRAFO_GantryCutter2 (FB) 带刀片旋向龙门
裁布机:刀片按XY走向,偏转刀片角度。 pi:目标位置矢量(x,y),插值器的输出 v:当前路径切线的矢量,插值器的输出 dOffsetX: x轴的附加偏移 dOffsetY…...

『PyQt5-Qt Designer篇』| 07 Qt Designer中栅格布局和表单布局的使用
07 Qt Designer中栅格布局和表格布局的使用 1 栅格布局1.1 按钮布局1.2 栅格布局中拖入控件1.3 保存并调用2 表单布局2.1 标签+输入控件2.2 保存并调用3 组合水平和垂直布局1 栅格布局 1.1 按钮布局 拖入几个按钮,如图: 选中所有按钮,右键点击布局-栅格布局: 之后可以看到…...

深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...

遍历 Map 类型集合的方法汇总
1 方法一 先用方法 keySet() 获取集合中的所有键。再通过 gey(key) 方法用对应键获取值 import java.util.HashMap; import java.util.Set;public class Test {public static void main(String[] args) {HashMap hashMap new HashMap();hashMap.put("语文",99);has…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
Python 包管理器 uv 介绍
Python 包管理器 uv 全面介绍 uv 是由 Astral(热门工具 Ruff 的开发者)推出的下一代高性能 Python 包管理器和构建工具,用 Rust 编写。它旨在解决传统工具(如 pip、virtualenv、pip-tools)的性能瓶颈,同时…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...