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

  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.rhkb.cn/news/108870.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[管理与领导-55]:IT基层管理者 - 扩展技能 - 1 - 时间管理 -2- 自律与自身作则,管理者管好自己时间的五步法

前言: 管理好自己的时间,不仅仅是理念,也是方法和流程。 步骤1:理清各种待办事项 当提到工作事项时,这通常指的是要完成或处理的工作任务或事务。这些事项可以包括以下内容: 任务分配:根据工作…

maven工程的目录结构

https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html maven工程的目录结构: 在maven工程的根目录下面,是pom.xml文件。此外,还有README.txt、LICENSE.txt等文本文件,便于用户能够…

疲劳检测-闭眼检测(详细代码教程)

简介 瞌睡经常发生在汽车行驶的过程中,该行为害人害己,如果有一套能识别瞌睡的系统,那么无疑该系统意义重大! 实现步骤 思路:疲劳驾驶的司机大部分都有打瞌睡的情形,所以我们根据驾驶员眼睛闭合的频率和…

PMP P-10 Purchase Management

竟然不让上传图片了。。为啥???【上传过于频繁】行吧

设计模式之九:迭代器与组合模式

有许多方法可以把对象堆起来成为一个集合(Collection),比如放入数组、堆栈或散列表中。若用户直接从这些数据结构中取出对象,则需要知道具体是存在什么数据结构中(如栈就用peek,数组[])。迭代器…

mongodb聚合排序的一个巨坑

现象: mongodb cpu动不动要100%,如下图 分析原因: 查看慢日志发现,很多条这样的查询,一直未执行行完成,占用大量的CPU [{$match: {"tags.taskId": "64dae0a9deb52d2f9a1bd71e",grnty: …

CentOS 7 Nacos 设置开机自动重启

一、说明 Nacos如果是手动启动的话,在服务器宕机或者重启后,没有自动运行,影响很多业务系统,需要每次手动执行命令 startup.sh -m standalone,才能启动 Nacos 服务,不能像docker服务一样,使用 …

校招算法题实在不会做,有没有关系?

文章目录 前言一、校招二、时间复杂度1、单层循环2、双层循环 三、空间复杂度四、数据结构五、校招算法题实在不会做,有没有关系?六、英雄算法集训 前言 英雄算法联盟八月集训 已经接近尾声,九月算法集训将于 09月01日 正式开始,目…

如何在windows电脑上安装多个node,并可以进行随意切换

一、进入官网http://nvm.uihtm.com/ 下载 二、启动解压后的程序 1.开始安装nvm 选择要安装的目录 一直下一步–下一步–最后点击完成 3.最后点击完成即可 ![在这里插入图片描述](https://img-blog.csdnimg.cn/3656568c7e9946e8a04219811fc4c4d3.png 三、在cmd控制台进行操作…

若依微服务版部署到IDEA

1.进入若依官网,找到我们要下的微服务版框架 2.点击进入gitee,获取源码,下载到本地 3.下载到本地后,用Idea打开,点击若依官网,找到在线文档,找到微服务版本的,当然你不看文档,直接按…

【DevOps视频笔记】4.Build 阶段 - Maven安装配置

一、Build 阶段工具 二、Operate阶段工具 三、服务器中安装 四、修改网卡信息 五、安装 jdk 和 maven Stage1 : 安装 JDK Stage 2 : 安装 Maven 2-1 : 更换文件夹名称 2-2 : 替换配置文件 settings.xml- 2-3 : 修改settings.xml详情 A. 修改maven仓库地址 - 阿里云 B…

【校招VIP】产品设计分析之思维整体性

考点介绍: 对于产品分析设计时需要全面的分析用户需求,而产品思维方式的核心是“以问题为核心”,即先多花时间搞清楚要解决的问题究竟是什么,要深入、全面的思考。 『产品设计分析之思维整体性』相关题目及解析内容可点击文章末尾…

Leetcode-每日一题【剑指 Offer 36. 二叉搜索树与双向链表】

题目 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。 为了让您更好地理解问题,以下面的二叉搜索树为例: 我们希望将这个二叉搜索树转化为双向循环链表…

什么是 TF-IDF 算法?

简单来说,向量空间模型就是希望把查询关键字和文档都表达成向量,然后利用向量之间的运算来进一步表达向量间的关系。比如,一个比较常用的运算就是计算查询关键字所对应的向量和文档所对应的向量之间的 “相关度”。 简单解释TF-IDF TF &…

Web安全测试(三):SQL注入漏洞

一、前言 结合内部资料,与安全渗透部门同事合力整理的安全测试相关资料教程,全方位涵盖电商、支付、金融、网络、数据库等领域的安全测试,覆盖Web、APP、中间件、内外网、Linux、Windows多个平台。学完后一定能成为安全大佬! 全部…

PHP8的匿名函数-PHP8知识详解

php 8引入了匿名函数(Anonymous Functions),它是一种创建短生命周期的函数,不需要命名,并且可以在其作用域内直接使用。以下是在PHP 8中使用匿名函数的知识要点: 1、创建匿名函数,语法格式如下&…

利用tidevice+mysql+grafana实现ios性能测试

利用tidevicemysqlgrafana实现ios性能测试 1.什么是tidevice? tidevice是一个可以和ios设备进行通信的工具,提供以下功能: 截图获取手机信息ipa包的安装和卸载根据bundleID 启动和停止应用列出安装应用信息模拟Xcode运行XCTest&#xff0c…

kafka-python 消费者消费不到消息

排除步骤1: 使用group_id”consumer_group_id_001“ 和 auto_offset_reset"earliest" from kafka import KafkaConsumerconsumer KafkaConsumer(bootstrap_servers["dev-kafka01.test.xxx.cloud:9092"],enable_auto_commitTrue, auto_commit…

网络直播源码UDP协议搭建:为平台注入一份力量

网络直播源码中的UDP协议的定义: UDP协议又名用户数据报协议,是一种轻量级、无连接的协议。在网络直播源码平台中,UDP协议有着高速传输与实时性的能力,尤其是在网络直播源码实时性要求较高的场景,UDP协议的应用有着重要…

Vue脚手架中安装ElementUi

目录 ElementUi简介: ElementUi下载: npm 安装: 引入ElementUi: 测试是否引入成功: Element-ui官网:组件 | Element ElementUi简介: ElementUi,是由国内的饿了么团队开发并开源的一套为开…