JVM知识点(一)

news2024/12/26 10:58:49

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)类加载过程

  1. 加载:将.class文件加载到JVM的方法区中;
  2. 验证:判断.class是否可以加载进入JVM中,不合法的直接抛出异常;低版本的JVM无法加载高版本类库;
  3. 准备:为类变量分配内存,并初始化值;此时实例对象还未分配内存,所以此时是在方法区中进行的;如下图,类变量有两次赋值,一次在准备阶段(赋予默认值),一次在初始化阶段(赋予程序员指定的值),而局部变量不会初始化赋值,没有默认值,不指定会报错;

  1. 解析:将符号应用替换为直接应用。符号应用就是我们定义的字面量,如int a=1;a就是符号应用;直接应用是直接执行目标对象的指针或偏移量;
  2. 初始化:初始化成员变量;类信息只会存储一份到方法区中,类加载只会加载一次,所以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时,需要整理内存,不能和用户进程并发,会造成停顿;

回收过程如下:

  1. 初始标记:只标记和GC Root直接相连的对象,因为GC Root在追踪活对象时最为耗时,同时还要标记与年轻代相关的对象;会发生STW;
  2. 并发标记:在初始标记阶段,进行并发标记(标记所有可达对象)。该阶段最为耗时,但可以和用户进程并行。在该阶段,可能会有对象引用发生变化,引用发生变化的对象会被标记为dirty,用于后续重新扫描;
  3. 并发预清理:并发预清理被标记为dirty状态的对象,将dirty状态对象重新标记,清除dirty状态,该阶段和用户进程并发,因此可能还会出现dirty状态;
  4. 并发可取消的预清理:重新标记阶段是需要STW(停止用户线程),因此在满足某些条件时,可以终止标记,避免会扫年轻代大量对象;
  5. 最终标记:第二次STW,标记所有存活对象。之前的并发预清理阶段可能发生多次,可能赶不上应用变化清况,所以最终标记会STW,停止应用进程,最终标记所有存活对象;
  6. 并发清除:与用户进程并发进行,清除所有不可达对象,可能会产生新的垃圾(浮动垃圾),该部分垃圾只能等到下一次GC时才能被回收;
  7. 并发重置:重置与CMS相关的内部结构,为下一次GC做准备;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/939793.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

四川玖璨电子商务有限公司:短视频运营表格

随着互联网的飞速发展和用户对内容需求的不断增加,短视频运营成为了当前互联网领域的一大热门。短视频作为一种具有高度吸引力和传播力的内容形式,成为各大平台争相追逐和竞争的热点。 然而,短视频运营并非一项简单的任务。为了能够在激烈的…

HOOPS Exchange如何实现3D模型格式转换?

HOOPS Exchange是一个专业的3D数据转换工具包,用于处理各种不同格式的3D模型数据。它提供了高效、精确的转换和处理功能,让开发者能够在不同的3D软件和环境之间无缝交换模型数据。 HOOPS Exchange将模型加载到标准化数据结构中,可以查询该数…

Android Update Engine 分析(二十一)Android A/B 更新过程

0. 背景 早期 Android A/B 系统升级在 Android 7.1 版本推出时,参考文档十分有限,也就是 Android 官方大概有两三个页面介绍文档。 我的第一篇 A/B 系统分析文章《Android A/B System OTA分析(一)概览》从总体上介绍了什么是 A/…

全面解析MES系统中的车间退料管理

一、车间退料管理的定义: 车间退料是指在生产过程中,将不合格或多余的物料、半成品或成品从车间环节返还到供应链的过程。车间退料管理则是指对这一退料过程进行规范化、系统化的管理和跟踪。 二、车间退料管理的流程: 1. 退料申请&#xf…

不会用这个工具,你的Linux服务器就是个摆设!

大家好,我的网工朋友 在运维这一块,没有工具可谓是寸步难行。 一个好的Linux运维,为了提升自己的工作效率,免不得会找一些适合自己业务需求的工具,用起来工作效率高,工作幸福指数直线上升。 今天整理了几…

数字化、智能化的酒店固定资产管理系统

酒店固定资产管理系统是一种专门为酒店行业定制的管理软件,可以帮助酒店管理者全面、准确地管理固定资产。该系统具有以下实际功能和特点:  资产库存功能:通过扫描二维码或手动输入条形码,完成酒店固定资产的有效总结&#xff0…

windows搭建向量数据库milvus

这里我们使用docker的方式,搭建本地向量数据库。 首先安装docker,windows下载安装docker。 下载链接:https://docs.docker.com/desktop/install/windows-install/ 安装完成后,开始菜单可以看到docker。 安装milvus 下载 YAML文…

探索内网穿透工具:实现局域网SQL Server数据库的公网远程访问方法

文章目录 1.前言2.本地安装和设置SQL Server2.1 SQL Server下载2.2 SQL Server本地连接测试2.3 Cpolar内网穿透的下载和安装2.3 Cpolar内网穿透的注册 3.本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 数据库的重要性相信大家都有所了解&…

可快速搭建运营的答题考试问卷调查小程序开发演示

考试答题问卷调查小程序,支持每一个用户自由发起考试答题、问卷调查。支持控制问卷搜集、回答等各个环节的设置。支持使用系统模板问卷,可以一键创建属于自己的问卷。支持考试答题中错题搜集和添加错题,巩固知识点。 核心亮点: …

《Linux从练气到飞升》No.18 进程终止

🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux菜鸟刷题集 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的…

前端 js实现 选中数据 动态 添加在表格中

如下图展示,表格上方有属性内容,下拉选中后,根据选中的内容,添加在下方的表格中。 实现方式,(要和后端约定,因为这些动态添加的字段都是后端返回的,后端自己会做处理&#xff0c…

C# textBox 右键菜单 contextMenuStrip

需求: 想在上图空白处可以右键弹出菜单,该怎么做呢? 1.首先,拖出一个 ContextMenuStrip。 随便放哪里都行,如下: 2.在textBox里关联这个“右键控件”即可,如下: 最终效果如下: 以上…

世微AP9234 升压型DC/DC LED恒流驱动

描述 AP9234是一款由基准电压源、振荡电路、误差放大电路、相位补偿电路、电流限制电路等构成的CMOS升压型DC/DC LED驱动。由于内置了低导通电阻的增强型N沟道功率MOSFET,因此适用于需要高效率、高输出电流的应用电路。另外,可通过在VSENSE端子连接电流…

述途路人团·百分之一的困

我开发的第2款Steam上的小游戏: 《述途路人团百分之一的困》(英文名称:《As Talk As Walk Wayfarer Team – One Percent Sleepy》) https://store.steampowered.com/app/2465530/_/ 电子邮件:atawwt_onepes163.com

关于cross entropy这个概念

第一步,现在果断上chatGPT去查准确概念: 也就是说,你有一个真实的概率分布P, 同时,你通过训练得到的一个模型,这个模型对于这个数据的输出的概率分布是Q, 而H(P,Q)就是用来描述这个2个概率分布…

实现不同局域网文件共享的解决方案:使用Python自带HTTP服务和端口映射

文章目录 1. 前言2. 本地文件服务器搭建2.1 python的安装和设置2.2 cpolar的安装和注册 3. 本地文件服务器的发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 数据共享作为和连接作为互联网的基础应用,不仅在商业和办公场景有广泛的应用…

成都爱尔李晓峰提醒发生麦粒肿要怎么“处理”

麦粒肿在临床上称为脸腺炎 (hordeolum ),就是生活中的“针眼”。 是一种眼脸腺体的急性化脓性炎症病变,具有急性炎症常表现出的红、肿、热、痛等典型症状,病变处有硬结,硬结破溃后排出脓液,多可自愈。 哪些情况易发麦粒…

linux操作系统的权限的深入学习

1.Linux权限的概念 Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制 普通用户:在linux下做有限的事情。 超级用户的命令提示符是“#”,普通用户…

如何下载免费文献

文章目录 一、支付宝参考 一、支付宝 白嫖知网的入口之一,就藏在你天天用的支付宝里。 首先,在支付宝上办理读者证: 搜索“浙江图书馆”,进入左下方功能栏里的“服务大厅”。 点击“新用户注册”填信息 办理完毕后&#xf…

Python 包管理(pip、conda)基本使用指南

Python 包管理 概述 介绍 Python 有丰富的开源的第三方库和包,可以帮助完成各种任务,扩展 Python 的功能,例如 NumPy 用于科学计算,Pandas 用于数据处理,Matplotlib 用于绘图等。在开始编写 Pytlhon 程序之前&#…