JVM总结

news/2024/3/29 20:08:10/文章来源:https://blog.csdn.net/qq_41595149/article/details/129251583

1. 内存结构

image-20230225154307114

线程私有区

程序计算器

  1. 作用:是一块较小的内存空间,存储的是当前线程所执行的字节码文件的序号
  2. 特点:线程私有,不会出现内存空间溢出

虚拟机栈

虚拟机栈是管理JAVA方法执行的内存模型,每个方法执行时都会创建一个当前栈桢,在当前栈桢里面存储方法的局部变量表,操作数栈,动态链接方法,返回值,返回地址等信息,栈大小决定了方法调用的可达深度(递归多少层,嵌套调用多少层其他方法,在idea中,-Xss参数可以设置虚拟机栈的大小)

  1. 是线程私有的
  2. 局部变量表存放了编译期可知的所有基本数据类型(byte,short,int,long,float,double,boolean,char),以及对象引用
  3. 栈太小或者方法调用过深都将抛出StackOverFlowError异常

本地方法栈

为本地语言服务的栈,Native方法服务

线程共享区

堆内存

存放对象实例的区域,对象,数组,以及常量池(从java7开始常量池也会使用堆内存)

堆内存从GC角度可以分为:新生类(Eden区,From Survision区和To Survision区),老年代,永久代(在Java8的时候被移除了)

特点:是线程贡献,需要考虑线程安全问题,同时会产生内存溢出问题

-Xms 设置最小堆内存大小(不能小于1024K)

-Xmx:设置最大堆内存大小(不能小于1024K)

方法区

用于存储已被虚拟机加载的类信息,常量,静态变量,即时编译器编译后代码等数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeNxJfex-1677507866501)(null)]

特点:

  1. 是一块线程共享的内存区域
  2. 方法区的大小决定了系统可以保存多少个类,如果系统定义了太多的类,导致方法区溢出,虚拟机同样会抛出内存溢出的错误
  3. 现在说的方法区一般是指元数据区(元空间Metaspace,java8的时候添加的),如果不指定大小,默认情况下,虚拟机会耗尽系统的可用内存

堆栈的区别

  • 存储的东西不同,栈内存存储局部变量和方法调用,而堆内存存储对象,包括成员变量,局部变量,还有类变量
  • 共享不同,栈内存是线程私有的,堆内存是所有线程共有的
  • 异常错误不同:栈空间不足:java.lang.StackOverFlowError,堆空间不足:java.lang.OutOfMemoryError。
  • 大小不同,栈空间小于堆空间

获取堆内存数据

java.lang.Runtime类中包含了与内存先关的方法

获取剩余空间的的字节数:Runtime.freeMemory()

获取总内存的字节数:Runtime.totalMenory()

2. 垃圾回收

垃圾回收机制

在Java中,程序员不需要显示的去释放一个对象的内存,而是由虚拟机自动去执行,在JVM中,有一个垃圾回收线程,他是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者在堆内存不足时才会触发执行,扫描那些被视为垃圾的对象,将他们添加到回收的集合中进行回收,也可以进行手动回收,通过System.gc(),通知GC运行

垃圾回收算法

  • 标记清除法

    标记出所有需要回收的对象,在标记完成后,统一回收的被标记的对象

    优点:速度比较快

    缺点:会产生内存碎片,碎片过多,仍会使得连续空间变少

  • 标记整理

    标记出所有需要回收的对象,在标记完成后统一进行整理,将存活对象进行一端移动,减少内存碎片,效率相对较低

    优点:无内存碎片

    缺点:效率较低

  • 复制算法

    开辟两份大小相等的空间,一份空间始终空着,垃圾回收时将存活对象拷贝进空闲时间

    优点:无内存碎片

    缺点:占用空间多

  • 分代回收

根据对象的存活周期不同,将对象划分为几块,比如堆内存的新生代和老年代,然后根据各个年代的特点采用最合适的算法进行回收;

新生代对象的存活时间比较短,因此使用的是复制算法,老年代对象存活的时间比较长,因此使用的是标记清除或者标记整理

分代垃圾回收的过程

分代垃圾回收器分两个区,新生代和老年代,新生代默认占1/3,老年代占2/3

新生代使用复制算法,新生代里面又分3个区(Eden,To Survivor, From Survicior)默认占比是8:1:1,当Eden区满了之后就会触发第一次MinorGC,将Eden区和From区存活的对象复制到To Survivor区域,然后to Survivor区域和From Survivor互换,原来的To Servivor区域成为下一次的From Survivor区域,然后清空Eden和From Survivor区中的对象,From中的对象每经过一次MinorGC,他的年龄值就会加1,达到15的移动到老年代,这里使用了复制算法,老年代满了或者是超过了临界值则会触发完全垃圾回收

判断一个对象是否为垃圾

引用计数法

堆中每个对象实例都有一个引用计数,当一个对象被创建的时候,且将这个对象实例分配给一个变量,该变量计数设置为1,当任何其他变量被赋值为这个对象的引用时,计数加1,但当一个对象实例的某个引用超过了生命周期,或者被设置为一个新值时,这个对象实例的引用计数将会减1,任何引用计数为0的对象都是可以被当做垃圾收集的对象,也就是一个垃圾,引用计数法容易产生循环引用的问题,如果两个对象相互引用,那么他们的引用就会一直存在,导致一直无法回收,为了解决这个问题,下面引入了可达性分析法

可达性分析算法

可达性分析算法又叫做根搜索法,就是通过一系列的称之为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索走过的路径被称为(Reference Chain)影响链,当一个对象到GC Roots没有任何链相连时(即从 GC Roots节点到该节点是不可达的),则这个对象就是可以被当做垃圾收集的对象,也就是一个垃圾

可以被作为GC Roots的对象

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

引用类型

  • 强引用

    默认声明的就是强引用,只要强引用存在,垃圾回收器就永远不会回收该对象,哪怕内存不足时也不会去回收,所以强引用是造成Java内存泄漏的主要原因,如果想回收某个对象可以将值赋值为null,切断强引用关系

  • 软引用:一些非必需但是仍有用的对象,通过SoftReference实现,在内存足够的时候,软引用对象不会回收,在内存不足时则会回收,当回收了软引用对象内存还是不足时,会抛出内存溢出异常

  • 弱引用:比软引用的引用强度更低一些,通过WeakReference实现,无论内存是否足够,JVM都会被进行垃圾回收

  • 虚引用:最弱的引用关系,通过PhantomReference类实现,每次垃圾回收都会被回收,主要用于跟踪对象的垃圾回收状态,虚引用的对象始终返回一个null,虚引用对象始终和引用队列一起使用,当一个对象还存在虚引用时,会被加入到引用队列中,jvm通过引用队列是否包含这个虚引用来了解这个对象是否将要被进行垃圾回收

3. 对象分配

对象在内存中如何分配

对象优先在Eden区中分配,当Eden区没有足够的空间时,会触发垃圾回收,把存活的对象移动到Survicor空间,如果Survivor空间也满了,则会把部分对象移动到老年代中,如果对象太大,是一个大对象,则这个对象会直接分配到老年代,不会发生GC

对象如何从年轻代进入老年代

  1. 对象年龄够老,默认是15,可以通过XX:MaxTenuringThreshold设置,复制到老年代,同时年龄加1
  2. 大对象会直接分配到老年代,大对象的定义和具体的JVM版本,堆大小,垃圾回收策略有关,一般为2K~128k,可以通过XX:pretenureSizeThreshold设置其大小

4. 垃圾收集器

JVM针对新生代和老年代分别提供了不同的垃圾收集器,新生代有Serial,ParNew,Parallel Scavenge,针对老年代提供的垃圾收集器有Serial Old,Parallel Old,CMS,还有针对不同区域的G1分区收集算法

常见的垃圾收集器

Serial垃圾收集器

Java虚拟机运行在Client模式下的新生代的默认垃圾收集器,基于复制算法实现,是一个单线程收集器,在进行垃圾收集时,必须暂停其他所有工作线程,直到垃圾收集结束,Serial收集器对于但CPU运行环境来说,没有线程交互开销,可以获得最高的单线程垃圾收集效率

ParNew垃圾收集器

Java虚拟机运行在Server模式下,新生代的默认垃圾收集器,基于复制算法实现,采用多线程模式工作,在进行垃圾收集时,必须暂停其他所有工作线程,直到垃圾收集结束,ParNew垃圾收集器默认开启与CPU同等数量的线程进行垃圾回收,可以通过-XX:ParallelGCThreads参数调节工作线程数

Parallel Scavenge垃圾收集器

Parallel Scavenge垃圾收集器是为提高新生代垃圾收集效率而设计的垃圾收集器,基于复制算法实现,采用多线程,在系统吞吐量上有很大的优化,,可以更高效的利用CPU完成垃圾回收任务,Parallel Scavenge提供了三个参数用于调节,控制垃圾回收的停顿时间及吞吐量,分别是:控制足最大垃圾收集停顿时间:-XX:MaxGCPauseMillis,控制吞吐量大小:-XX:GCTimeRation,控制是否开启自适应调节策略:UseAdaptiveSizePolicy

Serial Old垃圾收集器

Serial Old是JVM运行在Client模式下,老年代的默认垃圾收集器,Serial Old是Serial的老年代实现,同Serial一样采用单线程执行,不同的是Serial Old基于标记整理算法实现

Parallel Old垃圾收集器

Parallel Old垃圾收集器采用多线程并发进行垃圾回收,基于标记整理法实现,在设计上优先考虑系统吞吐量,其次考虑停顿时间等因素

CMS垃圾收集器

CMS垃圾收集器是为老年代设计的垃圾收集器,其主要目的是达到最短时间的垃圾回收停顿时间,基于线程的标记清除算法实现

CMS的垃圾回收过程包含4个过程

  1. 初始标记:只标记GC Roots直接关联的对象,速度很快,需要暂停所有工作线程
  2. 并发标记:和用户一起工作,执行GC Roots跟踪标记过程,不需要暂停工作线程
  3. 重新标记:为了确保并发标记的正确性,重新标记已经标记的对象,需要暂停工作线程
  4. 并发清除:和用户线程一起工作,执行清除GC Roots不可达对象的任务,不需要暂停工作线程

G1垃圾收集器

G1垃圾收集器是为了避免全区域垃圾收集引起的系统停顿,将堆内存划分大小固定的几个独立区域,独立使用这些区域跟踪垃圾收集进度,同时在维护一个优先级列表,根据系统允许的最长垃圾收集时间,优先回收垃圾最多的区域,相对于CMD垃圾收集器,G1垃圾收集器不产生内存碎片,可以精确的控制停顿时间,在不牺牲吞吐量的前提下实现停顿垃圾回收

G1垃圾回收器的几个重要参数

  1. -XX:MaxGCPauseMillis:暂停毫秒级,默认200毫秒
  2. -XX:G1HeapRegionSize:区域大小,默认最多2048块,每块的大小需要为2的幂次方,最大为32M
  3. -XX:G1NewSizePercent:新生代的最低百分比,默认5%
  4. -XX:G1MaxNewSizePercent:新生代的最大百分比,默认60%

5. 类加载器

类加载器负责将class文件加载到java虚拟机中,并为之创建一个Class对象

类加载器的流程

  1. 加载:将class文件加载到内存中,将静态数据结构转化成方法区中运行时的数据结构,在堆中生成一个代表这个类的对象,作为数据访问的入口
  2. 验证:确保加载的类符合JVM规范和安全,保证被效验类的方法在运行时不会做出危害虚拟机的事件,做一个安全检查
  3. 准备:为static变量在方法区中分配内存空间,设置变量的初始值,注意只设置静态变量,不包括实例变量,实例变量在对象初始化时赋值
  4. 解析:虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用中的符号可以是任何形式的字面量,只要能无歧义的定位到目标即可,直接引用是可以直接指向目标的指针,相对偏移量或者间接定位到目标的句柄
  5. 初始化:初始化类变量和其他资源
  6. 使用
  7. 卸载:GCh将对象从内存中卸载

常用的类加载器

  1. 启动类加载器(Bootstrap ClassLoader):虚拟机内置类加载器,加载java核心类库,如JAVA_HOME/jre/lib/rt.jar,resources.jar,sun.boot.class.path,使用C+/C++实现,他没有父类加载器,是扩展类加载器和应用程序加载器的父类加载器
  2. 扩展类加载器(Extension classLoader):由java语言编写,从系统属性java.ext.dirs目录中或者JDK安装目录JRE/lib/ext加载类库
  3. 应用程序类加载器(Application ClassLoader):应用程序类加载器,负责加载用户类路径上所指定的类,我们程序中默认的类加载器,可以通过ClassLoader#getSystemClassLoader()获取并操作这个加载器

双亲委派机制

双亲委派机制是当一个类加载器需要加载一个字节码文件时,首先会把这个任务委托给他的上级类加载器,上级类加载器又交给他的上级,以此递归,直到上级类加载器不能加载该字节码文件,然后再自己去加载这个字节码文件,如果自己也无法加载则抛出ClassNotFoundException异常,这样做的好处是防止一个字节码文件多次加载,保证了数据的安全性,即使重复加载了也不会是同一个class对象

6. JVM调优

在调优之前要先明确是否需要使用JVM调优,因为大多数Java应用是不需要调优的,大多数导致GC问题的原因是代码层面的问题,比如创建的对象数量过多,使用了大量的全局变量和大对象

需要调优的场景

  • Heap内存(老年代)持续上涨,达到设置的最大内存值
  • Full GC次数频繁
  • GC停顿时间过长
  • 应用出现OutOfMemory等内存异常
  • 系统吞吐量不高

调优步骤

  • 分析GC日志和Demp文件,判断是否需要优化,确定瓶颈问题点
  • 确定JVM调优量化目标
  • 调整JVM调优参数,包括内存,延迟,吞吐量
  • 观察调优前后的差异,不断分析和调整,找到最合适的参数
  • 将这些参数应用到服务器

调优参数

语法

  • -XX:+ ’ +'表示启用该选项
  • -XX:- '-'表示关闭该选项
  • -XX:= '='给选项设置一个数字类型的值,可跟随单位,例如:m和M表示兆字节,k和K表示k字节,g和G表示千兆字节

参数示例

-Xms4g:设置最小堆内存大小为4g,堆内存初始大小
-Xmx4g:设置最大堆内存大小为4g,堆内存最大值
-Xmn1200m:设置年轻代大小为1200MB,增大年轻代后,将会减小老年代的大小,这个值对系统性能较大,官方推荐配置为整个堆的3/8
-Xss512K:设置每个线程的堆栈大小,JDK5.0后每个线程堆栈大小为1MB,以前为256K,值越小,能创建的线程越多
-XX:NewRatio=4:设置年轻代与老年代的比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=8:设置年轻代中Eden区和Survivor区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:MaxTenuringThreshold=15:设置Survivor区中对象年龄的最大值为15,超过这个值则进入老年代,如果设置为0,则年轻代不经过Survivor,直接进入老年代
-XX:+UseParNewGC:年轻代
-XX:+UseConcMarkSweepGC:老年代

常用调优参数

-Xms:初始化堆内存大小,默认为物理内存的1/64(小于1G)
-Xmx:堆内存的最大值。默认(MaxHeapFreeRation参数可以调整)空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制
-Xmn:新生代大小,包括Eden和两个Survivor区
-XX:MaxDirectMemorySize=1G:直接内存,报java.lang.OutOfMemoryError:Direct buffer memory异常时可以上调这个值
-XX:+DisableExplicitGC:禁止运行期间显示的调用System.gc()来触发full GC
-XX:CMSlnitiatingOccupancyFraction=60:老年代内存回收阈值,默认为68
-XX:ConcGCThreads=4:CMS垃圾回收器并行线程数,推荐值为CPU核心数
-XX:ParallelGCThreads=8:新生代并行收集器的线程数
-XX:MaxTenuringThreshold=10:Survivor区的最大年龄值,超过就会进入老年代

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

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

相关文章

贴吧顶贴软件《今日/更新》

贴吧顶贴软件《今日/更新》百收贴吧工具箱,贴吧顶帖软件,贴吧推广引流神器#贴吧顶帖#贴吧推广 hello,大家好,我是软件的作者百收编辑狂潮老师。本次的视频讲解是作为一个百度顶贴的自动化脚本的视频安装教程和使用教程。你作为新…

SpringCloud(五)MQ消息队列

MQ概念常见消息模型helloworld案例实现实现spring AMQP发送消息实现spring AMQP接收消息工作消息队列实现发布订阅模型Fanout Exchange实现DirectExchange实现TopicExchange实现DirectExchange 和FanoutExchange的差异DirectExchange 和TopicExchange的差异基于RabbitListener注…

钉钉产品体验报告

一、调研的目的了解企业社交软件,借写竞品分析来帮助自己整理思路,看清市场的发展趋势;体验这类企业设计软件,掌握产品核心业务流程和产品结构,把握需求对应的功能点和界面结构,并侧面了解用户习惯&#xf…

用Python做数据分析有哪些优势?

众所周知,可以用作数据分析的语言有很多,包含Python、R语言等,而且Python被誉为数据分析的一大利器,更是该领域的首选语言,那么用Python做数据分析有哪些优势呢?跟着蛋糕往下看。 第一、Python语言自身的优势 Pytho…

ShardingSphere水平、垂直分库、分表和公共表

目录一、ShardingSphere简介二、ShardingSphere-分库分表1、垂直拆分(1)垂直分库(2)垂直分表2、水平拆分(1)水平分库(2)水平分表三、水平分库操作1、创建数据库和表2、配置分片的规则…

BigGAN

1、BIGGAN 解读1.1、作者 Andrew Brock、Jeff Donahue、Karen Simonyan 1.2、摘要 尽管最近在生成图像建模方面取得了进展,但从 ImageNet 等复杂数据集中 成功生成高分辨率、多样化的样本仍然是一个难以实现的目标。为此,我们以迄 今为止最大的规模训练生…

fastadmin:在新增页面,打开弹窗单选,参数回传

样式:核心代码:一、弹窗的控制器中:// 定义一个公共函数select(),如果这个请求是Ajax,则返回index()函数,否则返回view对象的fetch()函数。 public function select() {if ($this->request->isAjax(…

【软件测试】测试老鸟的迷途,进军高级自动化测试测试......

目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 很多从业几年的选手…

【阿旭机器学习实战】【37】电影推荐系统---基于矩阵分解

【阿旭机器学习实战】系列文章主要介绍机器学习的各种算法模型及其实战案例,欢迎点赞,关注共同学习交流。 电影推荐系统 目录电影推荐系统1. 问题介绍1.1推荐系统矩阵分解方法介绍1.2 数据集:ml-100k2. 推荐系统实现2.1 定义矩阵分解函数2.2 …

消息中间件的概念

中间件(middleware)是基础软件的一大类,属于可复用的软件范畴。中间件在操作系统软件,网络和数据库之上,应用软件之下,总的作用是为处于自己上层的应用软件提供运行于开发的环境,帮助用户灵活、高效的开发和集成复杂的…

ICA简介:独立成分分析

1. 简介 您是否曾经遇到过这样一种情况:您试图分析一个复杂且高度相关的数据集,却对信息量感到不知所措?这就是独立成分分析 (ICA) 的用武之地。ICA 是数据分析领域的一项强大技术,可让您分离和识别多元数据集中的底层独立来源。 …

PPP简介,PPP分层体系架构,PPP链路建立过程及PPP的帧格式

PPP(Point-to-Point Protocol)是一种用于在两个网络节点之间传输数据的通信协议。它最初是为在拨号网络上进行拨号连接而开发的,现在已经被广泛应用于各种网络环境中,例如在宽带接入、虚拟专用网(VPN)等场景…

【JAVA】一个项目如何预先加载数据?

这里写目录标题需求实现AutowiredPostConstruct实例CommandLineRunner实例ApplicationListener实例参考需求 一般我们可能会有一些在应用启动时加载资源的需求,局部或者全局使用,让我们来看看都有哪些方式实现。 实现 Autowired 如果是某个类里需求某…

[1]MyBatis+Spring+SpringMVC+SSM整合

一、MyBatis 1、MyBatis简介 1.1、MyBatis历史 MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。…

Vue中如何利用websocket实现实时通讯

首先我们可以先做一个简单的例子来学习一下简单的websocket模拟聊天对话的功能 原理很简单,有点像VUE中的EventBus,用emit和on传来传去 首先我们可以先去自己去用node搭建一个本地服务器 步骤如下 1.新建一个app.js,然后创建pagejson.js文…

【Linux】-- POSIX信号量

目录 POSIX信号量 sem_init - 初始化信号量 sem_destroy - 销毁信号量 sem_wait - 等待信号量(P操作) 基于环形队列的生产消费模型 数据结构 - 环形结构 实现原理 POSIX信号量 #问:什么是信号量? 1. 共享资源 -> 任何一…

【笔记】两台1200PLC进行S7 通信(1)

使用两台1200系列PLC进行S7通信(入门) 文章目录 目录 文章目录 前言 一、通信 1.概念 2.PLC通信 1.串口 2.网口 …

时间颗粒度选择(通过选择时间范围和颗粒度展示选项)

<template><div><el-time-selectplaceholder"起始时间"v-model"startTime":picker-options"startPickerOptions"change"changeStartTime"></el-time-select><el-time-selectplaceholder"结束时间&quo…

想招到实干派程序员?你需要这种面试法

技术招聘中最痛的点其实是不精准。技术面试官或CTO们常常会向我们吐槽&#xff1a; “我经常在想&#xff0c;能不能把我们项目中的代码打印出来&#xff0c;作为候选人的面试题的一部分&#xff1f;” “能不能把一个Bug带上环境&#xff0c;让候选人来试试怎么解决&#xf…

mysql中用逗号隔开的字段作查询用(find_in_set的使用)

mysql中用逗号隔开的字段作查询用(find_in_set的使用) 场景说明 在工作中&#xff0c;经常会遇到一对多的关系。想要在mysql中保存这种关系&#xff0c;一般有两种方式&#xff0c;一种是建立一张中间表&#xff0c;这样一条id就会存在多条记录。或者采用第二种方式&#xff…