JVM的原理与性能

news2024/11/28 6:38:06

1 JVM 内存结构

在这里插入图片描述

1.1 运行时数据区

1.1.1 栈(虚拟机栈)

每个线程在创建时都会创建一个私有的Java虚拟机栈,在执行每个方法时都会打包成一个栈帧,存储了局部变量表操作数栈动态链接方法出口等信息,然后放入栈中。方法的执行对应着栈帧出栈的过程。栈的大小默认为1M,可通过参数-Xss调整大小,如-Xss256k。

1.1.2 本地方法栈

与虚拟机栈类似,只是对应的是本地方法

1.1.3 程序计数器

程序计数器用于记录当前线程所执行的字节码指令的行号。

1.1.4 方法区

用于存储已经被虚拟机加载的类信息、常量、静态变量等数据。可通过参数(JDK1.8以后)-XX:MetaspaceSize, -XX:MaxMetaspaceSize调节,如-XX:MaxMetaspaceSize=3M。

1.1.5 堆

堆是Java中最大的一部分,它用于存储对象实例。所有的对象实例以及数组都在堆上分配内存。堆是由垃圾回收器自动管理的,因此开发者不需要关心内存的回收问题。但是,由于垃圾回收器的存在,堆上的数据可能会影响程序的性能。
可通过以下参数调节:-Xms:堆的最小值;-Xmx:堆的最大值;-Xmn:新生代的大小;-XX:NewSize:新生代最小值;-XX:MaxNewSize:新生代最大值;例如-Xmx256m堆划分为新生代和老年代,新生代又分为Eden区、Survivor1(from)区、Survivor2(to)区。

1.2 直接内存

调用native函数直接分配的堆外内存,使用直接内存避免了JAVA堆与native堆来回复制数据,能够提高效率。默认与-Xmx 参数值相同为 100M。可以通过-XX:MaxDirectMemorySize 来单独设置直接内存的大小。

1.3 Java的垃圾回收(Garbage Collection)机制

Java的垃圾回收是JVM自动回收不再使用的对象所占用的内存的过程。它减少了开发者手动管理内存的负担,避免了内存泄漏的问题。

垃圾回收器通过跟踪每个对象的引用关系来决定哪些对象是可达的,哪些对象是不可达的。当一个对象没有任何引用指向它时,它就被认为是不可达的,因此可以被垃圾回收器回收。

垃圾回收器的工作过程大致如下:

  • 从根对象(Root Object)开始遍历整个堆内存,标记所有被引用的对象;
  • 遍历整个堆内存,找到没有被标记的对象,将其回收;
  • 更新堆的大小。

在执行垃圾回收时,JVM会将所有线程暂停一段时间,这个过程被称为“Stop-The-World”。这可能会对程序的性能产生影响,因此优化垃圾回收器的性能是提高JVM性能的一个重要方向。

1.4 垃圾回收算法

1.4.1 标记-清除

首先标记所有需要回收的对象,在标记完成后,统一回收掉所有标记的对象(也可以标记存活对象,回收未标记的对象)。 缺点:执行效率不稳定(随着对象数量增长而降低)、内存空间碎片化。

1.4.2 标记-复制(简称复制算法)

将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块内存用完了,就将还存活的对象复制到另一块,然后再把已使用过的内存空间一次清理掉。

缺点:内存缩小为原来的一半。

商用虚拟机大多采用这种算法回收新生代,大多数对象是朝生夕死的(约98%对象熬不过第一轮收集),因此并不需要按照1:1来划分新生代的内存空间。

Appel式回收将新生代划分为一块较大的Eden区和两块较小的Survivor区,每次分配内存只使用Eden区和其中一块Survivor区, 发生垃圾回收时,将Eden区和Survivor区中仍然存活的对象一次性复制到另外一块Survivor区上,然后清理掉Eden区和使用过的Survivor区。HotSpot虚拟机默认Eden和Survivor的大小比例为8:1,只有一个Survivor空间,即10%的新生代会被浪费。任何时候都没法保证每次回收后只有少于10%的对象存活,当Suvivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(大多是老年代)进行分配担保。

1.4.3 标记-整理

标记-复制算法在对象存活率较高时要进行较多的复制操作,效率会降低。如果不想浪费50%的空间,就需要额外的空间进行分配担保,所以老年代不直接使用标记-复制算法。

标记-整理算法的标记过程与标记-清除算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象向内存空间的一端移动,然后清理掉边界以外的内容。

移动存活对象并更新所以引用这些对象的地方是一个极为负重的操作,必须全程暂停用户程序(称为Stop The World)才能进行。

1. 5 类加载的过程

1.5.1 加载

加载阶段,虚拟机需要完成三件事:

  • **获取类的二进制流:**通过类的全限定名获取类的二进制字节流。
  • 转化成运行时数据结构 将字节流所代表的静态存储结构转化为方法区的运行时数据结构。
  • 生成类对象: 在内存中生产一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据访问入口。

1.5.2 验证

是连接的第一步,目的是确保Class文件的字节流中包含的信息符合**《JAVA 虚拟机规范》**的全部约束,保证这些信息被当做代码运行后不会危害虚拟机自身的安全。 大致需要完成四个阶段的检验动作: 文件格式验证 验证字节流是否符合Class文件格式的规范,并且能被当前版本的虚拟机处理。验证包括:魔数、主次版本号、常量池等等。 元数据验证 对字节码描述的信息进行语义分析,以保证其描述的信息符合《JAVA 语言规范》的要求。验证包括:类是否有父类、类是否继承了不允许被继承的类、类的字段和方法是否与其父类产生矛盾等。 字节码验证 通过数据流分析和控制流分析,确定程序的语义是合法的、符合逻辑的。 符号引用验证 验证符号引用全限定名代表的类是否能够找到,对应的域和方法是否能找到,访问权限是否合法。

1.5.3 准备

为类中定义的类变量(被static修饰的变量)分配内存并设置初始值。通常情况下初始化是零值,只有变量被final修饰时,才会初始化为指定的值。

1.5.4 解析

将常量池中的符号引用替换为直接引用的过程。

1.5.5 初始化

根据程序员编码制订的主观计划区初始化类变量和其他资源。

1.6 类加载器

对于任意一个类,都必须由加载它的类加载器和这个类本身来确定其在JAVA虚拟机中的唯一性。每一个类加载器都都拥有一个独立的类名称空间。

主要有四种类加载器:

  • 启动类加载器(Bootstrap ClassLoader): 用来加载JAVA 核心库(JAVA_HOME\lib目录下)。

  • 扩展类加载器(Extension ClassLoader): 用来加载JAVA扩展库(JAVA_HOME\lib\ext目录下)。

  • 应用类加载器(Application ClassLoader,也称系统类加载器): 负责加载用户类路径(ClassPath)上的所有类库。可通过ClassLoader.getSystemClassLoader()来获取。如果应用中没有自定义的类加载器,一般情况下为应用程序中的默认类加载器。

  • 自定义类加载器(User ClassLoader): 通过继承java.lang.ClassLoader类实现。

1.7 双亲委派模式

如果一个类加载器接收到了加载类的请求,它首先不会去自己加载这个类,而是委托给父类加载器加载,依次递归。只有当父类加载器不能完成加载任务时,自己才去加载。 双亲委派模式的好处:JAVA中的类随着它的类加载器一起具备了一种带有优先级的层次关系。例如,java.lang.Object类,无论哪个类加载器加载,最终都会委托给最顶层的启动类加载器加载,因此Object类在程序的各种类加载器环境中都能保证是同一个类。

Tomcat打破双亲委派模型。

1.8 JVM性能优化

1.8.1 内存溢出

程序在申请内存时,没有足够的空间。情况如下:

  • 栈溢出。 循环调用(StackOverflowError)、线程太多(OutOfMemoryError)。

  • 堆溢出。 不断创建对象,分配大对象堆内存。

  • 方法区溢出。在经常动态生产大量 Class 的应用中,CGLIb 字节码增强,动态语言,大量 JSP(JSP 第一次运行需要编译成 Java 类),基于 OSGi 的应用(同一个类,被不同的加载器加载也会设为不同的类)。

  • 直接内存溢出。可以通过-XX:MaxDirectMemorySize来设置。

1.8.2 内存泄露

程序在申请内存后,无法释放已申请的内存。 可能存在内存泄露的情况:

  • 长生命周期的对象持有短生命周期的对象。如将ArrayList设置为静态变量,则容器中的对象在程序结束之前将不能被释放。

  • 连接未关闭。如数据库连接、网络连接、IO连接等。 变量作用作用域不合理 一个变量定义的作用范围大于其使用范围,并没有及时设置为null。

  • 内部类持有外部类。Java 的非静态内部类的这种创建方式,会隐式地持有外部类的引用,而且默认情况下这个引用是强引用,因此,如果内部类的生命周期长于外部类的生命周期,程序很容易就产生内存泄漏。

  • Hash值改变。在集合中,如果修改了对象中的那些参与计算哈希值的字段,会导致无法从集合中单独删除当前对象,造成内存泄露。

1.8.3 JVM 参数配置

-Xmx3550m: 最大堆大小为 3550m。
-Xms3550m: 设置初始堆大小为 3550m。
-Xmn2g: 设置年轻代大小为 2g。
-Xss128k: 每个线程✁堆栈大小为 128k。
-XX:MaxPermSize: 设置持久代大小为 16m
-XX:NewRatio=4: 设置年轻代(包括Eden 和两个Survivor 区)与年老代
的比值(除去持久代)。
-XX:SurvivorRatio=4: 设置年轻代中Eden 区与 Survivor 区的大小比值。
设置为 4,则两个 Survivor 区与一个 Eden 区✁比值为 2:4,一个 Survivor区占整个年轻代的 1/6
-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为 0
的话,则年轻代对象不经过 Survivor 区,直接进入年老代。

1.8.4 JDK提供的优化工具

    命令行工具:

    jps

    列出当前机器上正在运行的虚拟机进程。

    jps 显示进程和启动类名称

    jps –q 只显示进程

    jps –m 输出主函数传入的参数

    jps –l 输入主函数完整包名

    jps –v 输入启动程序制定的jvm参数

    jstat

    用于监视虚拟机各种运行信息的命令行工具。显示本地或远程虚拟机进程中的类装载、内存、垃圾收集、JIT(即时编译)等运行数据。

    用法:jstat –xx 进程号

    jstat –class 类加

    jstat –compiler JIT

    jstat –gc  GC堆状态

    jstat –gccapacity 各区大小

    jstat –gccause 最近一次gc统计和原因

    jstat –gcnew 新生代统 

    jstat –gcnewcapacity 新生代大小

    jstat –gcold 老年代统计

    jstat –gcoldcapacity 老年代大小

    jstat –gcpermcapacity 永久代大小

    jstat –gcutil gc统计汇总

    jstat –printcompilation 虚拟机编译统计

    jinfo

    查看和修改虚拟机参数

    用法:jinfo [option] <pid>

    jinfo –sysprops 获取虚拟机参数,等价于System.getProperties()

     jinfo –flag <name> 输出对应名称的参数

    jinfo –flag [+|-]<name> 开启或关闭对应名称的参数

    jinfo –flags 输出所有的参数

    jmap

    用于查看堆和永久代的详细信息、生成堆转储快照(dump文件)

    用法:jmap [option] <pid>

    jmap –heap 查看堆

    jmap –finalizerinfo查询 finalize 执行队列

    jmap –dump 导出堆dump文件 例如:jmap –dump:live –format=b,file=D:\heap.bin 1740

    jhat

    生成dump文件分析,可在浏览器上访问:http://localhost:7000

    jhat <dump文件>

    jstack

    查看虚拟机线程信息。

    可视化工具:

    jconsole

    JAVA_HOME\bin\jconsole.exe

    jvisualvm

    JAVA_HOME\bin\jvisualvm.exe

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

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

相关文章

关于我转生从零开始学C++这件事:获得神器

❀❀❀ 文章由不准备秃的大伟原创 ❀❀❀ ♪♪♪ 若有转载&#xff0c;请联系博主哦~ ♪♪♪ ❤❤❤ 致力学好编程的宝藏博主&#xff0c;代码兴国&#xff01;❤❤❤ 几天不见 &#xff0c;甚是想念&#xff01;哈咯大家好又是我大伟&#xff0c;五一的假期已经结束&#xff0…

rt-thread 挂载romfs与ramfs

参考&#xff1a; https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutorial/qemu-network/filesystems/filesystems?id%e4%bd%bf%e7%94%a8-romfs https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/tutor…

实操Linux磁盘管理(分区、格式化、挂载)

在Linux系统中&#xff0c;磁盘管理是一个必学的知识点。正确地进行磁盘分区、格式化和挂载可以确保我们能够充分利用磁盘空间并高效地存储和访问数据。 相比于Windows系统中的简单盘符管理&#xff0c;Linux中的磁盘管理更加复杂且灵活。在Linux系统中&#xff0c;一切设备都…

一款简易的免费抽奖软件

一、介绍 这款抽奖软件设计简洁&#xff0c;操作便捷。用户可以轻松将参与名单通过EXCEL文件导入至程序中&#xff0c;并可根据需要设定各类奖品和对应的中奖人数。在选定了奖品后&#xff0c;用户只需点击“开始”按钮&#xff0c;随后再按下“暂停”按钮&#xff0c;软件便会…

C++/Qt 小知识记录6

工作中遇到的一些小问题&#xff0c;总结的小知识记录&#xff1a;C/Qt 小知识6 dumpbin工具查看库导出符号OSGEarth使用编出的protobuf库&#xff0c;报错问题解决VS2022使用cpl模板后&#xff0c;提示会乱码的修改设置QProcess调用cmd.exe执行脚本QPainterPath对线段描边处理…

pycharm本地文件更新至虚拟机

tools–>deployment–>configuration root path的路径要跟远程路径对齐&#xff0c;方便后续运行 mapping映射&#xff0c;本地路径和远程路径 点击Browse 可以在右侧同步查看更新情况

【Element-UI快速入门】

文章目录 **Element-UI快速入门****一、Element-UI简介****二、安装Element-UI****三、引入Element-UI****四、使用Element-UI组件****五、自定义Element-UI组件样式****六、Element-UI布局组件****七、Element-UI表单组件****八、插槽&#xff08;Slots&#xff09;和主题定制…

VS项目Debug下生成的EXE在生产机器上运行

使用Visual Studio开发应用程序时&#xff0c;为了临时在非开发机上看一下效果&#xff0c;就直接把Debug下的文件全部拷贝到该机器上&#xff0c;直接双击exe运行。双击之后&#xff0c;没有直接打开应用程序&#xff0c;而是弹出了一个Error弹框。  赶快在网上搜了一遍&…

浏览器执行渲染原理

一、事件循环 事件循环&#xff08;Event Loop&#xff09;是JavaScript的执行环境的核心概念之一&#xff0c;它负责处理JavaScript中的异步操作和执行顺序。事件循环使得JavaScript能够在单线程上有效地处理并发&#xff0c;同时保持编程模型的简单性。 以下是事件循环的一…

智慧油田三维电子沙盘系统

深圳易图讯科技(www.3dgis.top)智慧油田三维电子沙盘系统采用三维GIS、大数据、云计算、虚拟现实、物联网、AI等前沿技术&#xff0c;支持无人机航拍、高清卫星影像、DEM高程数据、矢量数据、无人机倾斜摄像、BIM模型、点云、城市白模、等高线、标高点等数据融合和切换&#xf…

SCI论文发表:寻找论文选题的7种方法 (建议收藏)!

我是娜姐 迪娜学姐 &#xff0c;一个SCI医学期刊编辑&#xff0c;探索用AI工具提效论文写作和发表。 近期有好几个同学问娜姐关于论文选题的问题&#xff1a;什么样的选题是好的选题-有价值好发表。这篇来分享7种寻找有效选题的方法。 在我们寻找论文选题的过程中&#xff0c;…

猫头虎分享已解决Bug || **Babel转换器下载问题** Failed to resolve babel-loader dependency`

猫头虎分享已解决Bug &#x1f42f; || Babel转换器下载问题 &#x1f6ab;Failed to resolve babel-loader dependency 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a…

mapreduce | 自定义Partition分区(案例1)

1.需求 将学生成绩&#xff0c;按照各个成绩降序排序&#xff0c;各个科目成绩单独输出。 # 自定义partition 将下面数据分区处理&#xff1a; 人名 科目 成绩 张三 语文 10 李四 数学 30 王五 语文 20 赵6 英语 40 张三 数据 50 李四 语文 10 张三 英语 70 李四 英语…

BGP大型实验

一.实验拓扑 二.实验要求 1.As1中存在两个环回个地址为192.168.1.0/24&#xff0c;该地址不能在任何协议中宣告&#xff1b; AS3中存在两个环回&#xff0c;一个地址为192.168.2.0/24&#xff0c;不能在任何协议中宣告&#xff0c;最终要求这两个环回可以ping通&#xff1b; …

【备战软考(嵌入式系统设计师)】12 - 嵌入式系统总线接口

我们嵌入式系统的总线接口可以分为两类&#xff0c;一类是并行接口&#xff0c;另一类是串行接口。 并行通信就是用多个数据线&#xff0c;每条数据线表示一个位来进行传输数据&#xff0c;串行接口就是一根数据线可以来一位一位地传递数据。 从上图也可以看出&#xff0c;并行…

理解JavaScript递归

什么是递归 程序调用自身的编程技巧称为递归&#xff08;recursion&#xff09; 递归的基本思想是将一个复杂的问题分解成更小、更易于管理的子问题&#xff0c;这些子问题与原始问题相似&#xff0c;但规模更小。 递归的要素 基本情况&#xff08;Base Case&#xff09;&…

【linux】linux工具使用

这一章完全可以和前两篇文件归类在一起&#xff0c;可以选择放一起看哦 http://t.csdnimg.cn/aNaAg http://t.csdnimg.cn/gkJx7 拖更好久了&#xff0c;抱歉&#xff0c;让我偷了会懒 1. 自动化构建工具 make , makefile make 是一个命令&#xff0c;makefile 是一个文件&…

【RAG 论文】FiD:一种将 retrieved docs 合并输入给 LM 的方法

论文&#xff1a; Leveraging Passage Retrieval with Generative Models for Open Domain Question Answering ⭐⭐⭐⭐ EACL 2021, Facebook AI Research 论文速读 在 RAG 中&#xff0c;如何将检索出的 passages 做聚合并输入到生成模型是一个问题&#xff0c;本文提出了一…

VMware虚拟机故障:“显示指定的文件不是虚拟磁盘“,处理办法

一、故障现象 由于虚拟机宕机&#xff0c;强制重新启动虚拟机后显示错误&#xff0c;没有办法启动虚拟机。 虚拟机有快照&#xff0c;执行快照还原&#xff0c;结果也不行&#xff0c;反复操作&#xff0c;在虚拟机文件目录出现很多莫名文件 二、故障原因 根据故障提示&#…

浅谈@Controller注解和其他四大注解的区别

各位大佬光临寒舍&#xff0c;希望各位能赏脸给个三连&#xff0c;谢谢各位大佬了&#xff01;&#xff01;&#xff01; 目录 1.Spring五大注解的使用约定 2.Controller注解的特别之处 3.总结 1.Spring五大注解的使用约定 Spring的五大注解&#xff08;Controller&#x…