JVM Java虚拟机入门指南

文章目录

  • 为什么学习JVM
  • JVM的执行流程
  • JVM的组成部分
    • 类加载
    • 运行时数据区
    • 本地方法接口
    • 执行引擎
  • 垃圾回收
    • 什么样的对象是垃圾呢
    • 内存溢出和内存泄漏
    • 定位垃圾的方法
    • 对象的finalization机制
    • 垃圾回收算法
    • 分代回收
    • 垃圾回收器
  • JVM调优参数
  • JVM调优工具
  • Java内存泄漏排查思路
  • CPU飙高排查方案与思路

为什么学习JVM

  1. JVM是Java的运行环境,优点是一次编译,到处运行。这是因为JVM是运行在操作系统上的,无论在什么操作系统都可以执行,所以常说Java是跨平台性的。
  2. 学习JVM能更深入的理解Java这门语言,理解Java语言底层代码的执行过程,为后期写出优质代码做好准备。比如很多时候一个问题需要深入字节码层次去分析才能得到准确的结论,字节码就是JVM的一部分。并且项目上线去排查一些程序log日志中无法呈现的问题,如:内存溢出等。
  3. 相较于C/C++Java不需要手动的去进行垃圾回收,但是正因为Java将内存控制交给JVM,一旦出现内存泄漏和溢出方面的问题,如果不了解JVM,是很难进行排查的。

JVM的执行流程

程序在执行前先要把Java代码转换成字节码(.class)文件,JVM需要将字节码文件通过一定方式的类加载器(ClassLoader)把文件加载到内存的运行时数据区(Runtime Data Area),而字节码文件是JVM的一套指令集规范,并不能直接由底层操作系统区执行,因此需要特定的命令解析器**执行引擎(Execution Engine)将字节码翻译成底层系统指令再交给CPU去执行,这个过程中需要调用其他语言的接口本地库接口(Native Interface)**来实现整个程序的功能。

JVM的组成部分

类加载、运行时数据区(内存区域)、本地方法接口、执行引擎。
在这里插入图片描述

类加载

  • 加载:读取字节码文件,转换并存储,为每个类创建一个class类对象并存储在方法区中。

  • 链接:

    • 验证:检查被加载的类内部结构是否正确,对字节码文件格式进行验证,判断文件是否污染并对基本语法格式验证。
    • 准备:为静态的变量分配内存,并设置默认初始值,不包含使用final修饰的static常量。
    • 解析:将符号引用(方法名)转化为直接引用(使用指针指向地址),将字节码中的表现形式转为内存中的表现形式。
  • 初始化:类的初始化,为类中定义的静态变量进行赋值。

  • 类加载器分类:引导(启动)类加载器(C+)、扩展类加载器、应用程序类加载器(默认)、自定义类加载器。

  • 双亲委派机制及其打破:如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父加载器还存在其父加载器,则继续向上委托,最终将到达顶层的启动类加载器,如果父类加载器可以完成类的加载任务,就成功返回,若无法完成加载任务,子加载器才会尝试自己去加载。如果都加载失败,则抛出异常ClassNotFoundException。 目的:为了确保加载系统类。优点:安全,可以避免用户自己编写的类替换Java的核心类库,并避免类重复加载。 打破: 通过集成ClassLoader类,重写loaclClass/findClass方法,实现自定义类加载。Tomcat就是自定义的类加载。

运行时数据区

  • 程序计数器:线程私有的,内部保存的是字节码的行号,用于记录正在执行的字节码指令的地址。
    • 字节码的行号:Java代码运行时,编译后的字节码文件是一行一行执行的,PC计数器就是记录当前线程执行的行号的,目的是其他线程抢占该线程后,下次接着之前执行的位置执行。
  • Java虚拟机栈:线程私有的,随着线程创建而创建,随着线程销毁而死亡。每个线程在运行时所需要的内存就是虚拟机栈。每个栈都是由一个个栈帧组成,对应的每次方法调用占用的内存。每个栈帧:局部变量表,操作数栈,动态链接,方法返回地址。每个线程中只能有一个活动栈帧,对应着当前正在执行的那个方法。
  • 本地方法栈:本地方法栈和Java虚拟机栈发挥的作用相似,区别在于Java虚拟机栈为虚拟机执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务,也就是执行native()方法,这些方法是C、C++写的。
  • 堆:线程共享区域,主要保存对象的实例、数组等。
  • 方法区:共享的内存区域,主要用来存储类信息、即时编译器编译后的信息以及运行时常量池。JVM启动时创建,关闭JVM释放。
    • 常量池:是一张表,主要存储的是要执行的类名、方法名、参数类型、字面量等信息,JVM根据指令会在这张表中进行查找。
    • 运行时常量池:常量池是.class文件中的,当类被加载时,它的常量池信息会放入运行时常量池,并将里面的符号地址变为真实地址(#1 #2 之类的)

本地方法接口

简单地讲,一个 Native Method 是一个Java调用非Java代码的接囗。一个 Native Method 是这样一个Java方法:该方法的实现由非Java语言实现,比如C。特点:用native关键字修饰的方法称为一个本地方法,没有方法体。

为什么使用:因为Java在有些层次的任务使用Java实现起来不容易,Java语言需要与外部环境进行交互,直接访问操作系统接口即可,JVM本身开发也是在底层使用了C语言。

执行引擎

  • 解释器:解释器有两种 ,一种是古老的字节码解释器:在执行时通过纯软件代码翻译字节码的执行,效率非常低下。另一种现在普遍使用的模板解释器:将每一条字节码和一个模板函数相关联,模板函数中直接产生这条字节码执行时的机器码,提高了解释器的性能。
  • JIT即时编译器:可以将整个函数体编译成机器码,有效的避免函数体被解释执行,在重复执行时直接执行编译后的机器码即可,大大提升了执行效率。通俗的说就是如果遇到经常执行的字节码指令,只要执行过一次,将一些频繁执行的热点代码进行编译,并缓存到方法区中,后续再来执行就不需要翻译,可以直接取出对应的机器指令,性能更快,提高了执行效率。

垃圾回收

什么样的对象是垃圾呢

Java中的垃圾对象是指没有被任何引用变量所引用的对象。这些对象无法被访问,也无法被使用,因此它们占用内存空间而不被程序所使用,成为垃圾对象

内存溢出和内存泄漏

内存溢出指的是程序在申请内存时,由于没有足够的内存可用,而导致程序崩溃或者出现其他异常情况的现象。这通常是因为程序错误地使用了内存,例如未及时释放不需要的内存或者使用了太多内存资源,导致系统无法提供足够的内存来满足应用程序的需求。

内存泄漏指的是程序中存在一些对象或变量没有被垃圾回收器及时回收,导致这些对象一直占用着内存空间并最终耗尽可用内存的现象。通常是因为程序中存在不合理的设计或编码问题,例如忘记释放动态分配的内存、使用循环引用等等。还有就是打开了使用对象的东西,但是没有关闭,导致垃圾处理时认为对象处于运行状态,不会被回收处理,IO流close和jdbc链接close没有关闭。

两者区别在于,内存泄漏是程序代码中存在的开发问题,内存溢出则是由于系统资源有限造成的结果。需要解决内存泄漏问题,通常需要审查代码并进行调试,而需要解决内存溢出问题,则需要考虑优化应用程序,增加可用内存资源,并可能需要进行代码重新设计,以便更有效地使用和释放内存。

定位垃圾的方法

  • 引用计数法:当一个对象被引用了一次,就在当前对象头上递增一次引用次数,如果这个对象的引用次数为0,代表这个对象可回收。比如创建一个demo对象,在JVM内存中会在栈中存储一个变量然后指向在堆中开辟对的一块空间来存储这个对象,引用计数法会给堆中的对象添加一个引用的参数ref=1,当demo=null,此时栈中的变量不会指向内存中的对象,ref变为0。引用计数法原理简单,效率也很高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,主要原因是引用计数就很难解决对象之间相互循环引用的问题。当相互引用时ref次数增加了两次,此时如果demo=null,ref就会变为1,不会被识别为垃圾,这就是循环引用,会引发内存泄漏。

  • 根搜索算法:目前的虚拟机都是通过可达性分析算法来确定哪些内容是垃圾,核心思想是沿着GC Root对象,遍历寻找关联着的对象就不是垃圾对象,扫描过程中,不能GC Root访问到该对象的就是可以被回收的。

  • GC Root可以是:虚拟机栈(栈帧中的本地变量表)中引用的对象。方法区中类静态属性引用的对象。方法区中常量引用的对象。本地方法栈中JNI(Native方法)引用的对象。

对象的finalization机制

对象的 finalization 机制是一种内存管理模式,它允许程序在对象被垃圾回收之前执行特定的清理和释放操作。在Java中,finalize() 方法是用于实现对象的 finalization 机制的。当一个对象变为垃圾之前,JVM会在内部自动调用其 finalize() 方法(如果该对象的 finalize() 方法未被重写,则不会执行任何操作),并在 finalize() 方法执行结束之后回收该对象。开发人员可以在 finalize() 方法中编写释放资源、关闭打开的文件、清除临时数据等操作,以便程序尽快回收不再使用的内存空间。

垃圾回收算法

  • 标记清除: 使用GC Root标记处存活的对象,清除没有标记的对象。优点:标记和清除速度快。缺点:内存碎片化严重,内存不连贯。
  • 标记复制: 将内存区域分为两块,当使用GC Root标记出存活的对象,将这些对象复制到另外一块之前清空的区域中。优点:当垃圾对象多的时候效率高,清理后内存没有碎片。缺点:需要两块内存空间,同一时刻只能使用一块空间,内存使用率较低。
  • 标记整理:使用GC Root标记出存活的对象,清除没有标记的对象,将标记存活的对象向一端移动,避免了内存碎片化,但是由于移动,相较于标记清除性能是有一定影响。

分代回收

  • MinorGC(young GC):发生在新生代的垃圾回收,SWT时间短。

  • MixedGC:新生代+老年代垃圾部分区域垃圾回收,G1收集器特有。

  • FullGC:新生代+老年代完整垃圾回收,STW时间长,应尽量避免。

  • SWT(Stop The World):暂停所有应用程序线程,等待垃圾回收的完成。

垃圾回收器

  • 串行垃圾回收器: Serial和SerialOld,单线程垃圾回收,堆内存较小。Serial作用于新生代,采用标记复制算法。SerialOld作用于老年代,采用标记整理算法。工作原理:垃圾回收时只有一个线程在工作,并且需要SWT。
  • 并行垃圾回收器:Paraller New和Paraller Old,并行垃圾回收器。Paraller New作用于新生代,采用标记复制算法。Paraller Old作用于老年代,采用标记整理算法。这个垃圾回收器是JKD8中默认使用的,工作原理是垃圾回收时多个线程工作,Java应用中所有线程SWT。
  • CMS(并发)垃圾回收器:主要是针对老年代的垃圾回收器,并发执行的,使用标记清除的垃圾回收器,是一款以获取最短停顿时间为目标的收集器,停顿时间短用户体验是比较良好的,最大的特点是在进行垃圾回收时,应用仍能正常运行。主要过程:
    • 初始标记(SWT):标记直接与GC Root关联的对象 。
    • 并发标记:标记与GC Root间接关联的对象。
    • 重新标记:防止之前标记时有的垃圾被关联,漏标。
    • 并发清理
  • G1垃圾回收器:和其他垃圾回收器不同的是G1垃圾回收器是将堆区域分为多个区域,每个区域都可以充当eden、survivor、old、humongous(为大对象准备),采用的是标记复制算法进行垃圾回收。特点是响应时间与吞吐量兼顾,垃圾回收主要分为三个阶段新生代回收、并发标记、混合收集。如果回收的速度赶不上创建新对象的速度就会触发Full GC。
    • 新生代垃圾回收:新生代的内存区域一般在G1堆中分配5%-6%,如果达到这个区间就会触发垃圾回收,使用标记复制算法将存活的对象复制到幸存者区中(挑出一个空闲区域),需要暂停用户线程。有新对象创建会将一块区域创建为eden区进行存储,之后进行垃圾回收时会将eden和幸存者区中存活的对象复制到另一个区域(幸存者区),超过15次的对象会复制到创建的老年区中。
    • 并发标记:当老年代占总堆内存超过45%就会触发并发标记,并发标记就是将老年代中所有的存活对象标记出来,这个过程是并发的,无需暂停用户线程。
    • 混合收集:在并发标记之后,会有一个重新标记阶段,用来解决标记阶段的漏标问题,此时需要swt。在回收老年代时,并不是一次将所有的老年代区域进行垃圾回收,而是有一个人为设置的预期的暂停时间,根据这个暂停时间优先回收价值高的区域(标记期间存活对象少,这个也是G1名称的由来),将这些回收价值高的老年代以及伊甸园,幸存者区一同进行一次垃圾回收,这就是混合收集,然后将伊甸园区和幸存者区中存活的对象放入新创建的幸存者区中,将老年代中存活的对象放入新创建的老年区中。

JVM调优参数

  • 堆空间大小:-Xms -Xmx : 设置堆的初始大小和最大大小,为了防止垃圾收集器在初始大小和最大大小之间收缩堆,而产生额外的时间,通常将最大和初始大小设置为相同的值,不指定的话默认单位是字节。
    • 堆空间设置多少合适:一般最大大小默认为物理内存的1/4,初始大小是物理内存的1/64,堆太小的话,可能会频繁导致GC,会产生stw,暂停用户线程,对空间大肯定好,但是也有风险,假如发生Full GC扫描整堆空间,暂停用户进程时间较长。
  • 虚拟机栈的设置:-Xss : 默认值为1M,栈中一般存放栈帧,调用参数、局部变量表等,每个线程都会创建虚拟机栈,如果设置太大会导致线程数量减少,如果太小会导致栈内存溢出,一般建议设置256K或512K。
  • 年轻代和老年代大小比例:-XXSurvivorRatio=8 表示survivor:eden=2:8,这是默认的比例,我们也可以设置增大eden区的大小,用来减少YGC发生的次数,但是虽然减少了,但是eden区满时占用空间大,导致释放缓慢,此时STW时间较长,因此还是需要根据程序情况去调优。
  • 年轻代晋升老年代阈值:-XX:MaxTenuringThreshold=threshold 默认15,取值范围0-15
  • 设置垃圾回收器:-XX:+useParallerGC,-XX:+useParalloldGC,-XX:+useG1GC
    可以通过增大吞吐量来提高系统性能,可以通过这个设置并行垃圾回收器。

JVM调优工具

命令工具:jps查看进程状态、jstack查看进程内线程的堆栈信息、jmap查看堆转信息、jhat堆转储快照分析工具、jstatJVM统计检测工具。可视化工具:jconsole用于对JVM的内存线程,类的监控、
VisualVM能够监控线程内存情况。

  • 命令工具: jmap:通过jmap =heap pid 显示Java堆的信息

    jmap -dump:format=b,file=heap hprof pid,fomat=b表示以hprof=进制格式转Java堆的内存
    file= 用于指定快照dump文件的文件名
    使用以上命令生成一个进程或系统在某一时间的快照,比如在进程崩溃时甚至是任何时候,我们都可以通过工具将系统或进程的内存备份出来供调试分析使用。dump文件中包含了程序进行的模块信息,线程信息,堆栈调用信息,异常信息等数据,方便系统技术人员进行错误排查。

    jstat: jstat -gcutil pid 总结垃圾回收统计
    jstat -gc pid 垃圾回收统计

  • jconsole: 通过java/bin/jconsole.ext可以直接打开线程信息。

VisualVM:目前只有1.8中有,高版本没有,通过java/bin/jvisualvm.exe打开。

Java内存泄漏排查思路

  • 获取堆内存快照dump。

    • 使用jmap命令获取运行中程序的dump文件,有的情况是内存一处之后程序中断了但是jmap只能打印运行中的程序,所以可以通过使用Vm参数获取dump文件。
    • 使用VisualVM可以加载离线的dump文件。
  • VisualVM去分析dump文件。

  • 通过查看堆信息的情况去定位内存溢出的问题。

  • 找到对应代码,通过阅读上下文情况,进行修复即可。

CPU飙高排查方案与思路

  • 使用top命令查看哪一个命令占用CPU较高,可以拿到相应的pid。
  • 使用ps H =eo pid,tid,%cpu | grep 进程pid 可以找到进程中所有线程的信息。
  • 使用jstack 进程id 打印当前进程的所有线程信息,将刚才进程的线程id转换为16进制的线程id(打印的线程信息的id是16进制的),然后根据相应的线程id,定位到问题代码的代码行。

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

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

相关文章

私有化部署一个吃豆人小游戏

目录 效果 安装步骤 1.安装并启动httpd 2.下载代码 3.启动httpd 使用 效果 安装步骤 1.安装并启动httpd yum -y install httpd 2.下载代码 进入目录 cd /var/www/html/ 下载 git clone https://gitee.com/WangZhe168_admin/pacman-canvas.git 3.启动httpd syste…

c++阶梯之类与对象(中)< 续集 >

前文: c阶梯之类与对象(上)-CSDN博客 c阶梯之类与对象(中)-CSDN博客 前言: 在上文中,我们学习了类的六个默认成员函数之构造,析构与拷贝构造函数,接下来我们来看看剩下…

常用的EasyExcel表格处理-2(动态合并、自适应宽高)

EasyExcel官网:点击查看 1、动态合并单元格 此处主要根据自定义处理类ExcelFillCellMergeStrategy进行处理,具体内容可看代码注释。 1.1 前端调用controller PostMapping("/download/template")public void toDoExport(HttpServletResponse…

c#string方法对比

字符串的截取匹配操作在开发中非常常见,比如下面这个示例:我要匹配查找出来字符串数组中以“abc”开头的字符串并打印,我下面分别用了两种方式实现,代码如下: using System; namespace ConsoleApp23{ class Progra…

Android开发 button 按钮点击两次 响应onclick方法

问题 Android开发 button 按钮点击两次 响应onclick方法 详细问题 笔者xml代码 <!-- 一个按钮 --> <Button android:id"id/button1" android:layout_width"wrap_conten…

Rust 第一个rust程序Hello Rust️

文章目录 前言一、vscode 安装rust相关插件二、Cargo New三、vscode调试rustLLDB 前言 Rust学习系列。今天就让我们掌握第一个rust程序。Hello Rust &#x1f980;️。 在上一篇文章我们在macOS成功安装了rust。 一、vscode 安装rust相关插件 以下是一些常用的 Rust 开发插件…

相机图像质量研究(4)常见问题总结:光学结构对成像的影响--焦距

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

《Git 简易速速上手小册》第1章:Git 基础(2024 最新版)

文章目录 1.1 Git 简介&#xff1a;版本控制的演变1.1.1 基础知识讲解1.1.2 重点案例&#xff1a;协作开发流程优化案例&#xff1a;功能开发与分支策略 1.1.3 拓展案例 1&#xff1a;代码审查与合并1.1.4 拓展案例 2&#xff1a;冲突解决 1.2 安装和配置 Git&#xff1a;首次设…

Linux大集合

Linux Linux是什么&#xff1f; Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户、多任务、 支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和 64位硬件。 Linux内核 是一个Linux系统…

51单片机之数码管显示表白数字篇

朝菌不知晦朔 蟪蛄不知春秋 眼界决定境界 CSDN 请求进入专栏 是否进入《51单片机专栏》? 确定 目录 数码管的简介 数码管引脚定义 数码管的原理图 74HC245 代码实现 静态数码管的显示 动态数码管的显示 数码管实现表白画面 数码管的简介 L…

力扣面试题 05.06. 整数转换(位运算)

Problem: 面试题 05.06. 整数转换 文章目录 题目描述思路及解法复杂度Code 题目描述 思路及解法 1.通过将两个数进行异或操作求出两个数中不同的位(不同的位异或后为二进制1); 2.统计异或后不同的位的个数(即异或后二进制为1的个数) 复杂度 时间复杂度: O ( 1 ) O(1) O(1) 空间…

挑战杯 python+深度学习+opencv实现植物识别算法系统

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的植物识别算法研究与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;4分工作量&#xff1a;4分创新点&#xff1a;4分 &#x1f9ff; 更多…

2.3_9 吸烟者问题

2.3_9 吸烟者问题 问题描述 问题分析 假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它&#xff0c;但是要卷起并抽掉一支烟&#xff0c;抽烟者需要有三种材料&#xff1a;烟草、纸和胶水。三个抽烟者中&#xff0c;第一个拥有烟草、第二个拥有纸…

彩虹系统7.0免授权+精美WAP端模板源码

最低配置环境 PHP7.2 1、上传源码到网站根目录&#xff0c;导入数据库文件 2、修改数据库配置文件&#xff1a;/config.php 3、后台&#xff1a;/admin 账号&#xff1a; 4、前台用户&#xff1a;123456 密码&#xff1a;1234561

目标检测:2如何生成自己的数据集

目录 1. 数据采集 2. 图像标注 3. 开源已标记数据集 4. 数据集划分 参考&#xff1a; 1. 数据采集 数据采集是深度学习和人工智能任务中至关重要的一步&#xff0c;它为模型提供了必要的训练样本和测试数据。在实际应用中&#xff0c;数据采集的方法多种多样&#xff0c;每…

C语言--------指针(1)

0.指针&指针变量 32位平台&#xff0c;指针变量是4个字节&#xff08;32bit/84)--------x86 64位平台&#xff0c;指针变量是8个字节&#xff08;64bit/88)--------x64 编号指针地址&#xff1b;我们平常讲的p是指针就是说p是一个指针变量&#xff1b; ************只要…

VR全景技术可以应用在哪些行业,VR全景技术有哪些优势

引言&#xff1a; VR全景技术&#xff08;Virtual Reality Panorama Technology&#xff09;是一种以虚拟现实技术为基础&#xff0c;通过360度全景影像、立体声音、交互元素等手段&#xff0c;创造出沉浸式的虚拟现实环境。该技术不仅在娱乐领域有着广泛应用&#xff0c;还可…

操作系统透视:从历史沿革到现代应用,剖析Linux与网站服务架构

目录 操作系统 windows macos Linux 服务器搭建网站 关于解释器的流程 curl -I命令 名词解释 dos bash/terminal&#xff0c;(终端) nginx/apache&#xff08;Linux平台下的&#xff09; iis&#xff08;Windows平台下的&#xff09; GUI(图形化管理接口&#xff…

基于SpringBoot和PostGIS的震中影响范围可视化实践

目录 前言 一、基础数据 1、地震基础信息 2、全国行政村 二、Java后台服务设计 1、实体类设计 2、Mapper类设计 3、控制器设计 三、前端展示 1、初始化图例 2、震中位置及影响范围标记 3、行政村点查询及标记 总结 前言 地震等自然灾害目前还是依然不能进行准确的预…

日志报错 git -c dif.mnemonicprefix=false -c core.guotepath=false 解决方法

前言: 在进行下面操作前,必须确保,你是否安装了Git。 查看Git 在命令行窗口中输入`git --version`: 如果这个命令成功显示了Git的版本信息,这表明Git已经被安装。 1. 使用Sourcetree SourceTree 是 Windows 和Mac OS X 下免费的 Git 和 Hg 客户端…