【Java】JVM

news2024/12/22 23:59:47

一、介绍

1.什么是JVM?

  1. JVM是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的。
  2. JVM包含一套字节码指令集,一组寄存器,一个栈,一个垃圾回收堆和一个存储方法域。
  3. JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

在这里插入图片描述

JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

2.JDK、JRE、JVM是什么关系?

  1. JDK(Java Development Kit),Java 语言的软件开发工具包,是开发者用来编译、调试程序用的开发包。JDK是整个java开发的核心,它包含了JAVA的运行环境(JVM+Java系统类库)和JAVA工具。JDK也是Java程序需要在JRE上运行。

JDK包含的基本组件包括

  • javac – 编译器,将源程序转成字节码
  • jar – 打包工具,将相关的类文件打包成一个文件
  • javadoc – 文档生成器,从源码注释中提取文档
  • jdb – debugger,查错工具
  • java – 运行编译后的java程序(.class后缀的)
  • appletviewer:小程序浏览器,一种执行HTML文件上的Java小程序的Java浏览器。
  • Javah:产生可以调用Java过程的C过程,或建立能被Java程序调用的C过程的头文件。
  • Javap:Java反汇编器,显示编译类文件中的可访问功能和数据,同时显示字节代码含义。
  • Jconsole: Java进行系统调试和监控的工具
  1. JRE(Java Runtime Environment),也就是Java平台。所有的Java程序都要在JRE环境下才能运行。

JRE包括

  • 一个Java虚拟机(Java Virtual Machine,JVM)
  • 一些标准的类别函数库(Class Library
  1. JVM(Java Virtual Machine),是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

在这里插入图片描述

  • JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。
  • Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。

3.JVM原理

  1. JVM是Java的核心和基础,在Java编译器和OS平台之间的虚拟处理器,可在上面执行字节码程序。
  2. Java编译器只要面向JVM,生成JVM能理解的字节码文件。Java源文件经编译成字节码程序,通过JVM将每条指令翻译成不同的机器码,通过特定平台运行。

在这里插入图片描述

  • 线程私有区域:Java栈、本地方法栈、程序计数器;
  • 线程私有区域不会有垃圾回收;
  • 方法区属于特殊的堆;
  • JVM调优几乎都是调整堆;

4.JVM体系结构:

  • 类加载器:加载class文件;
  • 运行时数据区:包括方法区、堆、Java栈、程序计数器、本地方法栈
  • 执行引擎:执行字节码或者执行本地方法

在这里插入图片描述

二、运行时数据区

  • 方法区:属于共享内存区域,存储已被虚拟机加载的类信息常量静态变量即时编译器编译后的代码等数据。运行时常量池存在方法区中。

  • :Java虚拟机所管理的内存中最大的一块,一个JVM只有一个堆,堆的大小可以调节,唯一的目的是存放对象实例。由于是垃圾收集器管理的主要区域,因此有时候也被称作GC堆。

  • :用于描述Java方法执行的模型。每个方法在执行的同时都会创建一个栈帧,用于存储8大数据类型对象的引用局部变量表操作数栈动态链接方法出口等信息。每一个方法从调用至执行完成,对应于一个栈帧在虚拟机栈中从入栈到出栈。

  • 程序计数器:当前线程所执行字节码的行号指示器。每一个线程都有一个独立的程序计数器,线程的阻塞、恢复、挂起等一系列操作都需要程序计数器的参与,因此必须是线程私有的。

  • 本地方法栈:与虚拟机栈作用相似,只不过虚拟机栈为执行Java方法服务,而本地方法栈为执行Native方法服务,比如在Java中调用C/C++

三、类加载机制

类加载器通过一个类的全限定名来获取描述此类的二进制文件流的代码模块。

1. 类的生命周期(7个)

加载、验证、准备、解析、初始化、使用、卸载

2. 类加载的五个过程

虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、装换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型。

  • 加载:类加载器获取二进制字节流,将静态存储结构转化为方法区的运行时数据结构,并生成此类的Class对象,即把字节码通过二进制的方式转化到方法区中的运行数据区。

  • 验证:验证文件格式、元数据、字节码、符号引用,确保Class的字节流中包含的信息符合当前虚拟机的要求。

  • 准备:为类变量分配内存并设置其初始值,这些变量使用的内存都将在方法区中进行分配。

  • 解析:将常量池内的符号引用替换为直接引用,包括类或接口的解析、字段解析、类方法解析、接口方法解析。

  • 初始化:前面过程都是以虚拟机主导,而初始化阶段开始执行类中的 Java 代码。

3. 类加载器

  • 启动类加载器(BootStrap ClassLoader):主要负责加载jre/lib/rt.jar相关的字节码文件的。

  • 扩展类加载器(Extension Class Loader):主要负载加载 jre/lib/ext/*.jar 这些jar包的。

  • 应用程序类加载器(Application Class Loader):主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。

  • 自定义类加载器(User Class Loader):负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。

4. 类加载机制(双亲委派)

类的加载是通过双亲委派模型来完成的,双亲委派模型即为下图所示的类加载器之间的层次关系。

在这里插入图片描述

  • 工作过程:如果一个类加载器接收到类加载的请求,它会先把这个请求委派给父加载器去完成,只有当父加载器反馈自己无法完成加载请求时,子加载器才会尝试自己去加载。可以得知,所有的加载请求最终都会传送到启动类加载器中。

5. JVM类初始化顺序

  1. 父类静态代码块和静态成员变量
  2. 子类静态代码块和静态成员变量
  3. 父类代码块和普通成员变量
  4. 父类构造方法
  5. 子类代码块和普成员变量
  6. 子类构造方法

6. 对象的创建过程

  1. 检查:类是否已被加载,没有加载就先加载类

  2. 分配内存空间:使用 new 关键字在堆内存中分配一块空间,使用CAS方式分配,防止在为A分配内存时,执行当前地址的指针还没有来得及修改,对象B就拿来分配内存。

  3. 初始化对象头:在分配的内存空间中,Java 虚拟机会为对象头分配一定的空间,用于存储对象的元数据信息,如对象的类型、哈希码、GC 信息等。

  4. 执行构造方法:在对象头初始化完成后,Java 虚拟机会调用对象的构造方法,对对象进行初始化,包括成员变量的赋值、方法的调用等。

  5. 返回对象引用:构造方法执行完毕后,会返回对象的引用,该引用可以被赋值给变量,或者作为参数传递给其他方法。

7. 对象头中有哪些信息

对象头中有两部分:

  • 一部分是MarkWork,存储对象运行时的数据,如对象的hashcode、GC分代年龄、GC标记、锁的状态、获取到锁的线程ID等;
  • 另外一部分是表明对象所属类,如果是数组,还有一个部分存放数组长度。

四、垃圾回收

GC(Garbage Collection)垃圾收集,回收垃圾,释放内存。

程序计数器、虚拟机栈、本地方法栈是线程私有的,所以会随着线程结束而消亡。 Java 堆和方法区是线程共享的,在程序处于运行期才知道哪些对象会创建,这部分内存的分配和回收都是动态的,垃圾回收所关注的就是这部分内存。

GC的目的实现内存的自动释放,使用可达性分析法判断对象是否可回收,采用了分代回收思想,将堆分为新生代、老年代,新生代中采用复制算法,老年代采用整理算法,当新生代内存不足时会minorGC,老年代不足时会fullGC

1、判断对象已死

在进行内存回收之前要做的事情就是判断那些对象是‘死’的,哪些是‘活’的。

引用计数法
给对象中添加一个引用计数器,当一个地方引用了对象,计数加1;当引用失效,计数器减1;当计数器为0表示该对象已死、可回收;

注意:如果不下小心直接把 Obj1-reference 和 Obj2-reference 置 null。则在 Java 堆当中的两块内存依然保持着互相引用无法回收。引用计数法很难解决循环引用问题;

可达性分析
通过一系列的 ‘GC Roots’ 的对象作为起始点,从这些节点出发所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连的时候说明对象不可用。

可作为 GC Roots 的对象

  1. 虚拟机栈中引用的对象
  2. 方法区中类静态属性引用的对象
  3. 方法区中常量引用的对象
  4. 本地方法栈中native方法引用的对象

引用:下面四种引用强度依次减弱

  • 强引用:默认情况下,对象采用的均为强引用(new的对象)。哪怕内存溢出也不会回收
  • 软引用:SoftReference 类实现软引用。在系统要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行二次回收(只有内存不足时才会回收)。
  • 弱引用:WeakReference 类实现弱引用。对象只能生存到下一次垃圾收集之前。在垃圾收集器工作时,无论内存是否足够都会回收掉只被弱引用关联的对象。
  • 虚引用:PhantomReference 类实现虚引用。无法通过虚引用获取一个对象的实例,为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

什么情况下会内存溢出

  • 堆内存溢出:
  1. 当对象一直创建而不被回收时
  2. 加载的类越来越多时
  3. 虚拟机栈的线程越来越多时
  • 栈溢出:方法调用次数过多,一般是递归不当造成

2、垃圾收集算法

  • 标记清除算法:先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。
  • 复制算法:将可用内存分为大小相等的两块,每次只使用其中一块,当这一块内存用完了,就将存活的对象复制到另一块,最后- 将此块内存一次性清理掉。
  • 标记整理算法:先标记所有需要回收的对象,然后让所有存活的对象向一端移动,最后直接清理掉边界以外的另一端内存。
  • 分代收集算法:把Java堆分为新生代和老年代。新生代中只有少量对象会存活,就选用复制算法;老年代中对象存活率较高,选用标记清除算法。

3、垃圾收集器

  • Serial收集器:单线程收集器。收集垃圾时必须暂停其他所有工作线程,直到它收集结束。
  • Parnew收集器:Serial收集器多线程版本。
  • Parallel Scavenge收集器:使用复制算法的新生代收集器。
  • Serial Old收集器:使用标记-整理算法的老年代单线程收集器。
  • Parallel Old收集器:使用标记-整理算法的老年代多线程收集器。
  • CMS收集器:基于标记-清除算法的低停顿并发收集器。运作步骤为①初始标记②并发标记③重新标记④并发清除。
  • G1收集器:最前沿的面向服务端应用的垃圾收集器。运作步骤为①初始标记②并发标记③最终标记④筛选回收。

CMS:收集器有以下特点

  1. 以最小的停顿时间为目标,优先降低GC停顿时间(相对地降低吞吐量)。
  2. 只运行在老年代的垃圾回收器。
  3. 使用标记-清除算法,可以并发收集。

G1收集器有以下特点

  1. 并行与并发:无需停顿Java线程来执行GC动作。
  2. 分代收集:可独立管理整个GC堆。
  3. 空间整合:运行期间不会产生内存空间碎片。
  4. 可预测的停顿:除了低停顿,还能建立可预测的停顿时间模型。

4、JVM内存分代机制

方法区即被称为永久代,而堆中存放的是对象实例,为了回收的时候对不同的对象采用不同的方法,又将堆分为新生代和老年代,默认情况下新生代占堆的1/3,老年代占堆的2/3。
在这里插入图片描述

注意:逻辑上存在,物理上不存在

新生代(Young)
HotSpot将新生代划分为三块,一块较大的Eden(伊甸园)空间和两块较小的Survivor(幸存)空间,默认比例为8:1:1。

老年代(Old)
在新生代中经历了多次GC后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行GC的频率相对而言较低,而且回收的速度也比较慢。

永久代(Permanent):永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而言,一般而言不会进行垃圾回收。

元空间(metaspace)
从JDK 8开始,Java开始使用元空间取代永久代,元空间并不在虚拟机中,而是直接使用本地内存。那么,默认情况下,元空间的大小仅受本地内存限制。当然,也可以对元空间的大小手动的配置。

GC 过程
新生成的对象在Eden区分配(大对象除外,大对象直接进入老年代),当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC。

  • GC开始时,对象只会存在于Eden区和Survivor From区,Survivor To区是空的(作为保留区域)。
  • GC进行时,Eden区中所有存活的对象都会被复制到Survivor To区,而在Survivor From区中,仍存活的对象会根据它们的年龄值决定去向,年龄值达到年龄阀值(默认为15,新生代中的对象每熬过一轮垃圾回收,年龄值就加1,GC分代年龄存储在对象的Header中)的对象会被移到老年代中,没有达到阀值的对象会被复制到Survivor To区。接着清空Eden区和Survivor From区,新生代中存活的对象都在Survivor To区。
  • 接着,Survivor From区和Survivor To区会交换它们的角色,也就是新的Survivor To区就是上次GC清空的Survivor From区,新的Survivor From区就是上次GC的Survivor To区,总之,不管怎样都会保证Survivor To区在一轮GC后是空的。
  • GC时当Survivor To区没有足够的空间存放上一次新生代收集下来的存活对象时,需要依赖老年代进行分配担保,将这些对象存放在老年代中。

5、Minor GC、Major GC、Full GC之间的区别

Minor GC
Minor GC指新生代GC,即发生在新生代(包括Eden区和Survivor区)的垃圾回收操作,当新生代无法为新生对象分配内存空间的时候,会触发Minor GC。因为新生代中大多数对象的生命周期都很短,所以发生Minor GC的频率很高,虽然它会触发stop-the-world,但是它的回收速度很快。

Major GC
指发生在老年代的垃圾收集动作,出现了 Major GC,经常会伴随至少一次 Minor GC(非绝对),MajorGC 的速度一般会比 Minor GC 慢10倍以上。

Full GC
Full GC是针对整个新生代、老生代、元空间(metaspace,java8以上版本取代perm gen)的全局范围的GC。Full GC不等于Major GC,也不等于Minor GC+Major GC,发生Full GC需要看使用了什么垃圾收集器组合,才能解释是什么样的垃圾回收。

6、Minor GC、Major GC、Full GC触发条件

Minor GC触发条件
虚拟机在进行minorGC之前会判断老年代最大的可用连续空间是否大于新生代的所有对象总空间

  1. 如果大于的话,直接执行minorGC
  2. 如果小于,判断是否开启HandlerPromotionFailure,没有开启直接FullGC
  3. 如果开启了HanlerPromotionFailure, JVM会判断老年代的最大连续内存空间是否大于历次晋升(晋级老年代对象的平均大小)平均值的大小,如果小于直接执行FullGC
  4. 如果大于的话,执行minorGC

Full GC触发条件

  1. 老年代空间不足:如果创建一个大对象,Eden区域当中放不下这个大对象,会直接保存在老年代当中,如果老年代空间也不足,就会触发Full GC。为了避免这种情况,最好就是不要创建太大的对象。
  2. 方法区空间不足:系统当中需要加载的类,调用的方法很多,同时方法区当中没有足够的空间,就出触发一次Full GC
  3. 老年代最大可用连续空间小于Minor GC历次晋升到老年代对象的平均大小
  4. 调用System.gc()时(系统建议执行Full GC,但是不必然执行)

五、JVM监控和调优

工具:

  • Eclipse里面有 Eclipse Memory Analyzer tool(MAT)插件可以测试
  • Idea中也有这么一个插件,就是JProfiler

命令:

-Xms 设置初始化内存分配大小,默认 1/64
-Xmx 设置最大分配内存,默认 1/4
-XX:+PrintGCDetails 打印GC垃圾回收信息
-XX:+HeapDumpOnOutOfMoryError OOM DUMP

-Xmx[]:堆空间最大内存

-Xms[]:堆空间最小内存,一般设置成跟堆空间最大内存一样的

-Xmn[]:新生代的最大内存

-xx:[survivorRatio=3]:eden区与from+to区的比例为31,默认为41

-xx[use 垃圾回收器名称]:指定垃圾回收器

-xss:设置单个线程栈大小

一般设堆空间为最大可用物理地址的百分之80

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

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

相关文章

面向对象设计模式:创建型模式之抽象工厂模式

一、抽象工厂模式,Abstract Factory Pattern 1.1 Definition 定义 抽象工厂模式是围绕一个抽象工厂(其他工厂的工厂)创建其他工厂的创建型模式。 1.2 Intent 意图 Provide an interface for creating families of related or dependent o…

【AutoSAR】【MCAL】Dio

一、结构 二、功能介绍 DIO(数字输入输出)驱动模块主要是对端口(Port),通道(Channel)和通道组(ChannelGroup)进行读写操作。 通道(Channel)&…

Tomcat服务器配置以及问题解决方案

文章目录01 Tomcat简介02 Tomcat的安装03 Tomcat的使用启动Tomcat服务器 (解决一闪而过)测试 Tomcat 是否启动Tomcat 服务器的关闭04 Tomcat的配置配置端口控制台配置(乱码解决)部署工程到Tomcat中01 Tomcat简介 Tomcat是一款开源…

Android Compose——一个简单的Bilibili APP

Bilibili移动端APP简介依赖效果登录效果WebView自定义TobRow的Indicator大小首页推荐LazyGridView使用Paging3热门排行榜搜索模糊搜索富文本搜索结果视频详情合集信息Coroutines进行网络请求管理,避免回调地狱添加suspendwithContextGit项目链接末简介 此Demo采用A…

Motor-DK (MM32SPIN05PF, MM32SPIN06PF, MM32SPIN07PF)

输入电压范围:12V - 30V 使用 60V / 40A N-MOS 管 使用内建(MM32SPIN2x)/外挂(MM32SPIN05 / MM32SPIN06 / MM32SPIN07)GBW 6MHz 高速运放 x 4 MCU 使用 5V 供电 支持 48 / 64 Pin MM32SPIN 系列 MCU 支持无霍尔&#x…

LearnDash测验报告如何帮助改进您的课程

某一个场景。Pennywell 大学有一门课程“Introduction to Linear Algebra”。上学期进行了两次测验。20% 的学生在第一次测验中不及格,而 80% 在第二次测验中不及格。在进一步评估中,观察到第一次测验不及格的学生在第二次测验中也不及格。在第二次测验中…

基于Linux系统-搭建Java Web开发环境

目录 1. 安装JDK 2.安装MySQL数据库 3.安装Tomcat 4.访问Tomcat 1. 安装JDK 1.执行以下命令,查看yum源中JDK版本。 yum list java* 2.执行以下命令,使用yum安装JDK1.8。 yum -y install java-1.8.0-openjdk* 3.执行以下命令,查看是否安…

【软件使用】MarkText下载安装与汉化设置 (markdown快捷键收藏)

一、安装与汉化 对版本没要求的可以直接选择 3、免安装的汉化包 1、下载安装MarkText MaxText win64 https://github.com/marktext/marktext/releases/download/v0.17.1/marktext-setup.exe 使用迅雷可以快速下载 2. 配置中文语言包 中文包下载地址:GitHub - chi…

TPU编程竞赛系列|算能赛道冠军SO-FAST团队获第十届CCF BDCI总决赛特等奖!

近日,第十届中国计算机学会(CCF)大数据与计算智能大赛总决赛暨颁奖典礼在苏州顺利落幕,算能赛道的冠军队伍SO-FAST从2万余支队伍中脱颖而出,获得了所有赛道综合评比特等奖! 本届CCF大赛吸引了来自全国的2万…

【MySQL】查询访问方法

查询语句经过查询优化器生成 SQL 执行计划,在引入索引的情况下,MySQL 不可能让我们什么查询都是走全表扫描,那样效率太低了,所有需要有各种各样的执行计划 , MySQL 会根据经验为我们的查询语句生成它认为最优的执行计划…

mac安装nvm

1、nvm介绍 (1)什么是nvm?简单来说,nvm是一款可以用命令行快速切换node版本的工具! (2)为什么要切换node版本?打个比方,你目前正在用node 14版本,现在出了nod…

Greenplum-主备同步机制

我们在学习Greenplum的架构时知道,Greenplum中主要有Master管理层和Segment计算层。在高可用方面,Master通过配置一个Standby来实现主备,Segment则通过对实例设置镜像的方式也实现主备高可用(其中主实例称为Primary,备…

网络基础(二)

目录 应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面…

传输线的物理基础(二):信号在传输线中的速度

铜中电子的速度信号在传输线上传输的速度有多快?如果人们经常错误地认为信号在传输线上的速度取决于导线中电子的速度。凭着这种错误的直觉,我们可能会想象降低互连的电阻会提高信号的速度。事实上,典型铜线中电子的速度实际上比信号速度慢约…

OpenWrt 软路由 IPV6 配置 DDNS

一、申请 dynv6 账号 1、去官网注册一个账号,不过人机验证那块需要 "梯子" 才能注册成功 Free dynamic DNS for IPv6 2、注册成功后,创建一个 Domain 3、这是我创建好的 4、获取 密码,后面需要用到 二、配置 DDNS 1、点击服务菜…

C++之单例模式

目录 1. 请设计一个类,只能在堆上创建对象 2. 请设计一个类,只能在栈上创建对象 3.请设计一个类,不能被拷贝 C98 C11 4. 请设计一个类,不能被继承 C98 C11 5. 请设计一个类,只能创建一个对象(单例模式) 设计…

在GoLand中编译cgo程序

GoLand的C使用问题为什么会出现fyne之类的包无法在GoLand中运行安装gcc安装make安装Choco安装指令使用Choco安装make将make加入Path将GoLand的make路径进行替换测试,编译fyne程序问题 本人go萌新,正在学习golang的界面包fyen,但是在使用GoLa…

Android面试题——JVM

Android平台的虚拟机是基于栈的吗? JVM运行时数据区 运行时栈 基于栈的虚拟机 第0行表示将一个Int型的1推送至操作数栈栈顶,程序计数器指向第0行。第一行字节码表示将栈顶的int型数值存入第一个本地变量,这两行代码就进行了给局部变量赋值的操…

【开源硬件】STM32F030R8T6系统板

【开源硬件】STM32F030R8T6系统板✅STM32F030R8T6系统板兼容极海APM32F030R8T6 🔰支持stm32cubemx工程配置成STM32F030R8T6生成的MDK工程,经过Keil编译后可以直接使用ST-Link v2烧录器上传到极海APM32F030R8T6芯片当中,完全做到平替使用&…

[Java·算法·中等]LeetCode22. 括号生成

每天一题,防止痴呆题目示例分析思路1题解1分析思路2题解2分析思路3题解3👉️ 力扣原文 题目 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 示例 输入:n 3 输出&…