《你不会还没入门jvm调优吧》之前置知识

news2024/11/18 7:44:43

该文章为科普文,所以很多细节涉及不到,旨在指引入门,同事在聊的时候不至于插不上话,顺带回顾部分JVM相关知识。准备好了吗,开始发车。如有不正确的地方,欢迎批评指正。

目录

JVM调优调的到底是什么

回顾相关JVM的知识

我的对象在哪里?运行时数据区

Methoad Area

PC Register

本地方法栈

直接内存

我的对象分配在哪?内存分配策略

凭什么我是垃圾,他不是。什么算垃圾

引用计数法

可达性分析法

垃圾是如何被回收的?

垃圾回收触发条件

垃圾回收算法

垃圾的终结者,垃圾收集器

JVM调优调的到底是什么

不会还有人不知道什么是JVM吧?

调优的最终目的都是为了应用程序使用最小的硬件消耗来承载更大的吞吐量。所以我们可以通过调整堆分配的大小以及垃圾回收对象的方式,让其高效释放掉内存空间达到我们的目的。

一般情况下我们的调优目标是:

  • 堆内存使用率 <= 70%;
  • 老年代内存使用率<= 70%;
  • avg pause <= 1秒;
  • Full GC 次数 0 或 avg pause interval >= 24小时 ;
  • 创建更多的线程

如果程序运行时jvm的内存与我们的调优目标不符或者过于离谱,我们则需要调优。

jvm调优是不得已手段,一般来说jvm默认配置是够我们用的,我们在调优之前要优先架构调优和代码调优。

小结:我们调优调的是内存分配 + 垃圾回收。

回顾相关JVM的知识

我的对象在哪里?运行时数据区

供各线程共享的运行时内存区域,对于大多数应用来说,是 Java 虚拟机所管理的内存区域中最大的一块。几乎所有的对象实例和数组实例都要在 Java 堆上分配,但随着 JIT 编译器及逃逸分析技术的发展,也可能会被优化为栈上分配。

Heap 中除了作为对象分配使用,还包含字符串字面量 常量池(Internd Strings) 。 除此之外 Heap 中还包含一个 新生代(Yong Generation) 、一个 老年代(Old Generation) 。Java堆是垃圾收集器管理的主要区域,因此很多时候也被称做“GC堆”。

新生代分三个区,一个Eden区,两个Survivor区,大部分对象在Eden区中生成。Survivor 区总有一个是空的。

老年代中保存一些生命周期较长的对象,当一个对象经过多次的 GC 后还没有被回收,那么它将被移动到老年代。

Java 堆不需要连续内存,并且可以通过动态增加其内存,增加失败会抛出 OutOfMemoryError 异常。

Methoad Area

方法区的数据由所有线程共享,因此为安全的使用方法区的数据,需要注意线程安全问题。

方法区主要保存类级别的数据,包括:

  • ClassLoader Reference

  • Runtime Constant Pool

    • 数字常量
    • 类属性引用
    • 方法引用
  • Field Data:每个类属性的名称、类型等

  • Methoad Data:每个方法的名称、返回值类型、参数列表等

  • Methoad Code:每个方法的字节码、本地变量表等

当线程启动时,都会分配一个独立的运行时栈,用以保存方法调用。每个方法调用,都会在栈顶增加一个栈帧(Stack Frame)。

每个栈帧都保存三个引用:本地变量表(Local Variable Array)操作数栈(Operand Stack)当前方法所属类的运行时常量池(Runtime Constant Pool)

栈的大小可以动态扩展,默认1MB,但是如果一个线程需要的栈大小超过了允许的大小,就会抛出 StackOverflowError

PC Register

每个 JVM 线程,当线程启动时,都会有一个独立的 PC(Program Counter) 计数器,用来保存当前执行的代码地址(方法区中的内存地址)。如果当前方法是 Native 方法,PC 的值为 NULL。一旦执行完成,PC 计数器会被更新为下一个需要执行代码的地址。

本地方法栈

本地方法栈和 Java 虚拟机栈的作用相似,Java 虚拟机栈执行的是字节码,而本地方法栈执行的是 native 方法。

直接内存

使用过NIO的小伙伴们都知道这个玩意,它可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆里的 DirectByteBuffer 对象作为这块内存的引用进行操作。

我的对象分配在哪?内存分配策略

大多数情况下,对象在新生代 Eden 区分配。因为有JIT优化的逃逸分析,未逃逸的对象也会分配到栈上。

对象在 Eden 出生并经过 Minor GC 依然存活,将移动到 Survivor 中,年龄就增加 1 岁,增加到一定年龄则移动到老年代中。大对象是指需要连续内存空间的对象,最典型的大对象是那种很长的字符串以及数组,直接在老年代分配,避免在 Eden 区和 Survivor 区之间的大量内存复制。

凭什么我是垃圾,他不是。什么算垃圾

jvm如何鉴别你的对象是不是垃圾以便回收。

引用计数法

给每个对象添加一个引用计数器,当一个地方引用它的时候,计数器值加1;当引用失效后,计数器值减1。但是这种方法有一个致命的缺陷,当两个对象相互引用时会导致这两个都无法被回收

可达性分析法

 追踪从根结点开始的 引用图。起点即 GC Root,GC Root 根据 JVM 实现不同而不同,但是总会包含以下几个方面(堆外引用):

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

引用图是一个有向图,其中节点是各个对象,边为引用类型。JVM 中的引用类型分为四种:强引用(StrongReference)软引用(SoftReference)弱引用(WeakReference)虚引用(PhantomReference)

  • 强:禁止引用目标被垃圾收集器收集
  • 软:垃圾收集器在要发生内存溢出前将这些对象列入回收范围中进行回收
  • 弱:垃圾收集器在 GC 的时候会回收所有的 WeakReference
  • 虚:所有的虚引用都必须由程序明确的清除。同时也不能通过虚引用来取得一个对象的实例,使用虚引用的目的就是为了得知对象被GC的时机。虚引用有一个很重要的用途就是用来做堆外内存的释放,DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。你不可不知的Java引用类型之——虚引用 - 弗兰克的猫 - 博客园 (cnblogs.com)

垃圾是如何被回收的?

垃圾回收触发条件

Young GC(Minor GC) :只收集 Young Gen 的 GC,当 Eden 区空间不够时,发起 Minor GC。

Old GC:只收集 Old Gen 的 GC。只有 CMS的 Concurrent Collection 是这个模式。它主要是定时去检查 Old Gen 的使用量,当使用量超过了触发比例就会启动一次 GC,对 Old Gen做并发收集。

Mixed GC:收集整个 Young Gen 以及部分 Old Gen 的 GC。只有 G1 有这个模式。

Full GC(Major GC) :收集整个堆,包括 Young Gen、Old Gen、Perm Gen(如果存在的话)等所有部分的 GC 模式。1、当准备要触发一次 Young GC 时,如果发现之前 Young GC 的平均晋升大小比目前 Old Gen剩余的空间大,则不会触发 Young GC 而是转为触发 Full GC。2、调用 System.gc()。3、老年代空间不足。

垃圾回收算法

  • 复制回收算法:内存使用率低,将可用内存分为大小相等的两份,在同一时刻只使用其中的一份。当这一份内存使用完了,就将还存活的对象复制到另一份上,然后将这一份上的内存清空。复制算法能有效避免内存碎片。
  • 标记清除算法:产生大量不连续的内存碎片。先暂停整个程序的全部运行线程,让回收线程以单线程进行扫描标记,并进行直接清除回收,然后回收完成后,恢复运行线程。
  • 标记整理算法:可以集成空闲空间,与标记清除算法类似,但是回收期间同时会将保留的存储对象搬运汇集到连续的内存空间
  • 增量回收:将内存空间分成若干分区(Region)。程序运行所需的存储对象会分布在这些分区中,每次只对其中一个分区进行回收操作,从而避免程序全部运行线程暂停来进行回收,降低回收时间,增加程序响应速度。
  • 分代回收思想:还记得上边说过的新生代和老年代吗,就是对于不同生命周期的对象进行回收,不同的生命周期也可以采用不同的垃圾回收算法,以提高效率。

垃圾的终结者,垃圾收集器

连线表示垃圾收集器可以配合使用,常见的配合下图所示

简单介绍

  • Serial 收集器:它是单线程的收集器,只会使用一个线程进行垃圾收集工作。它的优点是简单高效,对于单个 CPU 环境来说,由于没有线程交互的开销,因此拥有最高的单线程收集效率。它是 Client 模式下的默认新生代收集器。

  • ParNew 收集器:它是 Serial 收集器的多线程版本。是 Server 模式下的虚拟机首选新生代收集器,除了性能原因外,主要是因为除了 Serial 收集器,只有它能与 CMS 收集器配合工作。

  • Parallel Scavenge 收集器:与 ParNew 一样是多线程收集器。其它收集器关注点是尽可能缩短垃圾收集时用户线程的停顿时间,而它的目标是达到一个可控制的吞吐量,它被称为“吞吐量优先”收集器。这里的吞吐量指 CPU 用于运行用户代码的时间占总时间的比值。

  • Serial Old收集器:是 Serial 收集器的老年代版本,也是给 Client 模式下的虚拟机使用。

  • Parallel Old 收集器:是 Parallel Scavenge 收集器的老年代版本。

  • CMS 收集器:

    • 四个流程如下:

      • 初始标记: 仅仅只是标记一下 GC Roots 能直接关联到的对象,速度很快,需要停顿。
      • 并发标记: 进行 GC Roots Tracing 的过程,它在整个回收过程中耗时最长,不需要停顿。
      • 重新标记: 为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要停顿。
      • 并发清除: 不需要停顿
    • 具有以下缺点:
      • 吞吐量低: 低停顿时间是以牺牲吞吐量为代价的,导致 CPU 利用率不够高。
      • 无法处理浮动垃圾,可能出现 Concurrent Mode Failure。浮动垃圾是指并发清除阶段由于用户线程继续运行而产生的垃圾,这部分垃圾只能到下一次 GC 时才能进行回收。由于浮动垃圾的存在,因此需要预留出一部分内存,意味着 CMS 收集不能像其它收集器那样等待老年代快满的时候再回收。如果预留的内存不够存放浮动垃圾,就会出现 Concurrent Mode Failure,这时虚拟机将临时启用 Serial Old 来替代 CMS。
      • 标记 - 清除算法导致的空间碎片,往往出现老年代空间剩余,但无法找到足够大连续空间来分配当前对象,不得不提前触发一次 Full GC
  • G1 收集器:是一款面向服务端应用的垃圾收集器,在多 CPU 和大内存的场景下有很好的性能。G1 把堆划分成多个大小相等的独立区域(Region),新生代和老年代不再物理隔离。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。
    • 流程:
      • 初始标记
      • 并发标记
      • 最终标记: 为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程的 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs 的数据合并到 Remembered Set 中。这阶段需要停顿线程,但是可并行执行。
      • 筛选回收: 首先对各个 Region 中的回收价值和成本进行排序,根据用户所期望的 GC 停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分 Region,时间是用户可控制的,而且停顿用户线程将大幅度提高收集效率。
    • 特点:
      • 空间整合: 整体来看是基于“标记 - 整理”算法实现的收集器,从局部(两个 Region 之间)上来看是基于“复制”算法实现的,这意味着运行期间不会产生内存空间碎片。
      • 可预测的停顿: 能让使用者明确指定在一个长度为 M 毫秒的时间片段内,消耗在 GC 上的时间不得超过 N 毫秒。

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

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

相关文章

Linux(CentOS 7)--gdb的基本调试指令

一下面的代码为例介绍一下linux中&#xff0c;gdb调试的基本指令 创建一个文件myfile.c&#xff0c;文件代码内容如下 1 #include <stdio.h>2 3 int Add(int x, int y)4 {5 6 return xy; …

高性能消息队列中间件MQ_part2

接上一篇part1的内容 RabbitMQ通配符模式_编写消费者 接下来我们编写通配符模式的消费者&#xff1a; // 站内信消费者 public class Customer_Station {public static void main(String[] args) throws IOException, TimeoutException {// 1.创建连接工厂ConnectionFactory…

springboot引入flink,maven打包插件需替换

目录说明说明 springboot引入flink后&#xff0c;如果要打包&#xff0c;传统的maven不行&#xff0c;要更换指定插件 <build><finalName>flink</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><art…

CMake 混编c和c++代码

准备工作 wsl 或者 有linux 系统(购买阿里云或者其他云服务器&#xff09;cmake, gcc, git 等一些必要的软件安装 环境 windows 下 的 wsl wsl 安装下载 例子 拿 Unix网络编程 举例, 作者对原生接口进行了封装, 我们需要编译使用在自己的工程 1. 创建空文件 cd E:\githu…

网络流量监控对OA系统性能分析案例

需求简介 某外高桥公司的OA系统是其重要的业务系统&#xff0c;OA系统负责人表示&#xff0c;部分用户反馈&#xff0c;访问OA系统时比较慢。需要通过分析系统看一下实际情况。 报告内容 本报告内容主要为&#xff1a;OA性能整体分析 分析时间 报告分析时间范围为&#xf…

同一条好友邀请信息给大量的人发,会导致领英账号被封吗?

做外贸的领英新人经常有一个问题&#xff1a;领英上添加好友时&#xff0c;同一条好友邀请信息给大量的人发&#xff0c;会导致领英账号被封吗&#xff1f; 这是一个被一部分人所忽略&#xff0c;也在被一部分人所担心的问题&#xff0c;因为很多领英新手都是在复制粘贴发送相…

游戏开发者的视觉盲区

本文首发于微信公众号&#xff1a; 小蚂蚁教你做游戏。欢迎关注领取更多学习做游戏的原创教程资料&#xff0c;每天学点儿游戏开发知识。嗨&#xff01;大家好&#xff0c;我是小蚂蚁。前天我刚发布了一个新的游戏作品——经典宝石方块。仍然是掌机模式&#xff0c;仍然是简约风…

JAVA开发(Web应用境外访问慢问题)

背景&#xff1a; 最近公司做的小程序出现在香港地区访问慢的问题。因为我们的应用是部署在大陆的腾讯服务器&#xff08;北京&#xff09;上&#xff0c;所以在香港地区访问大陆应用会比较慢。初步体验是4-5秒的响应速度。 影响的原因&#xff1a; 1、网络的原因&#xff0…

【HBase高级】7. HBase调优、常见问题处理

HBase调优 6.1 通用优化 NameNode的元数据备份使用SSD 定时备份NameNode上的元数据 每小时或者每天备份&#xff0c;如果数据极其重要&#xff0c;可以5~10分钟备份一次。备份可以通过定时任务复制元数据目录即可。 为NameNode指定多个元数据目录 使用dfs.name.dir或者dfs…

4.5--贪心--单源最短路径问题

设置顶点集合S并不断地作贪心选择--&#xff08;不属于这个集合S中距离"源"最短的顶点&#xff09;来扩充这个集合--更新最短距离 这张图需要放在最前面&#xff0c;就是经典dijkstra的主要思想。 为什么这样贪心是对的&#xff1f; 1、问题描述 给定带权有向图G (…

C++ 入门

C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等 文章目录一、命名空间二、输入输出三、缺省参数四、函数重载五、引用1. 引用的用法2. 常引用3. 引用的使用场景4. 引用的底层实现六、内联函数七、auto 关…

数组

循环队列中元素个数计算方法是固定的&#xff0c;即(尾-头)%长度&#xff0c;但是由于是循环队列所以尾可能会小于头&#xff0c;所以要加上长度&#xff0c;使尾-头保持是正整数&#xff0c;然后再对长度求余&#xff0c;即元素个数。循环队列中&#xff1a;头指针指向队列头元…

基于Java+Spring+Html的图书借阅管理系统详细设计和实现

博主介绍&#xff1a;✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

seo:百度统计

一、百度统计官网https://tongji.baidu.com/web5/welcome/login二、理解百度统计个人理解&#xff0c;添加这段代码到网站首页&#xff0c;有人访问该网站&#xff0c;即会加载这段代码&#xff0c;接着把信息发送到百度统计id 对应的百度统计账号&#xff0c;可从百度统计查看…

OpenMMLab AI实战课笔记 -- 第2节课

OpenMMLab AI实战课笔记 -- 第2节课1. 第二节课(图像分类)1.1 深度学习模型1.2 网络进化过程1.3 ResNet &#xff08;残差网络&#xff09;1.4 卷积的参数量1.5 卷积的计算量&#xff08;乘加次数&#xff09;1.6 降低模型参数量和计算量的方法1.7 可分离卷积1.8 注意力机制 At…

常见正则表达式使用参考

目录 一、正则函数 1.REGEXP 2.regexp_replace 3.regexp_extract 二、正则表达式 三、特殊字符转义 一、正则函数 1.REGEXP 语法格式&#xff1a; A REGEXP B &#xff08;A是需要匹配的字符串&#xff0c;B是正则表达式字符串&#xff09; 操作类型: strings 描述: …

UniTask详解

前言 UniTask为Unity提供一个高性能&#xff0c;0GC的async/await异步方案。 基于值类型的UniTask和自定义的 AsyncMethodBuilder 来实现0GC使所有 Unity 的 AsyncOperations 和 Coroutines 可等待基于 PlayerLoop 的任务( UniTask.Yield, UniTask.Delay, UniTask.DelayFrame…

哈工大机器学习复习笔记(四)

本篇文章是在参考西瓜书、PPT课件、网络上相关博客等资料的基础上整理出的机器学习复习笔记&#xff0c;希望能给大家的机器学习复习提供帮助。这篇笔记只是复习的一个参考&#xff0c;大家一定要结合书本、PPT来进行复习&#xff0c;有些公式的推导最好能够自己演算一遍。由于…

Scala 简单实现数据库连接池

在使用JDBC的时候&#xff0c;连接池是非常宝贵的资源。为了复用这些资源&#xff0c;可以将连接保存在一个队列中。当需要的时候可以从队列中取出未使用的连接。如果没有可用连接&#xff0c;则可以在一定时间内等待&#xff0c;直到队列中有可用的连接&#xff0c;否则将抛出…

浅谈估值模型:PB指标与剩余收益估值

摘要及声明 1&#xff1a;本文简单介绍PB指标的推导以及剩余收益的估值方式&#xff1b; 2&#xff1a;本文主要为理念的讲解&#xff0c;模型也是笔者自建&#xff0c;文中假设与观点是基于笔者对模型及数据的一孔之见&#xff0c;若有不同见解欢迎随时留言交流&#xff1b…