JVM从入门到入土之实战G1垃圾回收器(二)

简介: 前言文本已收录至我的GitHub仓库,欢迎Star:github.com/bin39232820…种一棵树最好的时间是十年前,其次是现在

絮叨


前面的章节


回顾


上面一篇文章大家其实可以搞懂G1的动态内存管理策略,它会根据情况动态的把Regiuon分配给 新生代,Eden,Survivor,老年代和大对象,但是新生代和老年代有一个各自最大的占比,然后在新生代Eden满的时候,触发新生代垃圾回收。

新生代的垃圾回收还是采用了复制算法,只不过会考虑预设的GC停顿时间,保证垃圾回收的停顿时间不难超过设置的时间,因此会挑选一些Region来进行垃圾回收。

然后跟之前说的一样,如果一些对象在新生代熬过拉框一定次数的GC,或者触发了动态年龄判定规则,则会进入老年代

而大对象则是进入单独的Regin,不再进入老年代

所以实际上在G1中,还是会存在新生代的对象会因为各种情况进入老年代


什么时候G1触发新生代+老年代混合垃圾回收?

G1 有个参数 是-XX:InitiatingHeapOccupancyPercent 它的默认值是45%

意思是说 如果老年代占了 堆内存的45%的Region的时候,此时就会触发混合回收阶段 如下图


G1垃圾回收的过程

首先会触发一个 初始标记的操作,这个过程需要 Stop the world 的,但是这个过程很快

如下图,先停止系统程序的运行,然后对各个线程栈内存中的局部变量代表GC Roots,以及方法区代表静态变量的Roots,进行扫描,标记出他们直接引用对象。


接下来就会进入到 并发标记的阶段,这个阶段会允许系统程序继续运行,同时进行GC Root追踪,如下图所示


这个并发标记阶段还是很耗时的,因为要追踪全部的存活对象

但是这个阶段是可以跟系统程序并发进行的,所以对系统程序的影响不太大

而且JVM会对并发标记阶段对象做出一些记录,比如哪个对象新建了,哪个对象失去引用了,等等

接着下一个阶段就是 最终标记阶段,这个阶段也会进入Stop the world ,系统程序是禁止运行的,但是会根据并发标记记录那些 对象修改,最终标记哪些对象存活,如下图所示


最后一个阶段 是混合回收阶段,这个阶段会计算老年代每个Region中的存活对象数量,存活对象占比,还有执行垃圾回收的预期性能和效率。

接着会停止系统程序,然后全力以赴尽快进行回收,此时会选择部分Region进行回收,因为必须让垃圾回收的停顿时间控制在我们指定的范围内。

比如说 老年代此时有1000Region都满了,但是因为设置的时间,只能停顿200ms,不那么我只能回收800个,那么就会只回收800个Region,把GC的时间控制在我们指定的范围之内,如下图


案例背景引入



这是一个百万级注册用户的在线教育平台,主要目标用户群体是几岁到十几岁的孩子,注册用户大概是几百万的规模,日活跃规模大概在几十万。

系统的业务流程其实也不复杂,而且我们可以排除掉一些选课,排课,浏览课程详情这些低频行为。

为啥呢?因为它不是一个电商平台,不是说每个人进去都会去浏览课程详情, 所以 一般的业务流程就是,有人进来浏览一下,考虑一段时间,然后买拉课程,所以它的高频行为是什么呢?我想了一下,应该是上课

也就是说,孩子们白天要在学校上课,一般也是晚上 8 9点的样子,是这个平台最活跃的时候,还有就是周末也是最活跃的时候,


所以晚上二三个小时的时间段,将会是平台的高峰期,而且白天几乎没有什么流量,可能90%的流量读在晚上,如下图所示


系统核心业务流程

接着我们来明确一下,这样的一个系统,孩子们在上课的时候主要高频的使用哪些功能呢

其实非常的简单,现在如果大家家里有孩子,平时对一些在线教育App有一定的了解的话,应该知道现在在线App都会主打互动环节

给大家举个例子,比如说给五六岁的孩子上的幼儿园英语课,大家觉得还会像以前一样吗,机械的跟读嘛

那肯定不是了,现在尤为强调的是在欢快的愉快的游戏中进行教学,让孩子们快乐的学习英语,数学之类学科的知识

所以说  在那几十万用户 晚上最高峰的时间使用系统上课的时候,尤为核心的业务流程就是大量的游戏互动环节


也就是说,这个游戏互动功能,一定会承担用户高频点击,大量的互动点击

比如在完成什么任务的时候必须点击很多的按钮,频繁的进行互动,然后系统后台需要大量的接收大量的互动请求,并记录用户互动的结果,

系统得记录下来用户完成了多少个任务,作对了几个,做错了几个。


系统的运行压力


现在我们开始来分析一下这个系统运行时候对内存使用的一个压力

其实核心点就是搞明白晚上二三个小时高峰期内,每秒钟会有多少请求,每个请求会连带产生多少对象,占用多少内存,每个请求处理多长时间。

首先我们来分析一下晚上高峰期内几十万用户同时在线使用平台,每秒钟会产生多少请求?

我们可以大致来估算一下,比如说晚上3小时高峰期内总共60万活跃用户,平均每个用户大概会使用1小时左右来上课,一个小时内会进行60次互动操作

那么20W活跃用户因为需要大量的互动操作,所以大致可以认为是每分钟进行1次互动操作,一小时内会进行60次互动操作

那么20万用户在1小时内会进行1200万次互动操作,平均每秒大概是3000次左右的互动操作,这个是一个很合理的数字

那么每秒要承载3000并发请求,根据经验来看  一般核心系统需要部署5台 4核8G的机器来抗住是差不多的,每台机器能抗住600请求,这个压力可以接受,一般不会导致宕机问题。


那么每个请求会产生多少个对象呢?

一次互动请求不会有太复杂的对象,他主要是记录一些用户互动过程的,可能会跟一些积分类的东西有关联

所以大致估算一下,一次互动请求大致会连带创建几个对象,占多大的内存,比如我们就认为是5kb吧那么一秒600请求会占用3MB左右的内存


G1垃圾回收的默认内存布局

接着我们来看看G1垃圾回收器的默认内存布局,我们采用4核8G的机器来部署系统,然后每台机器每秒会有600个请求会占用3Mb左右的内存空间。

那么假设我们对机器上的JVM,分配4G给堆内存,其中新生代默认初始占比5%,最大占比60%,每个Java线程的栈内存为1MB,元数据区域的内存为256M,此时的JVM参数如下

-XX:G1NewSizePercent 参数是用来设置新生代初始占比的,不用设置,维持默认值5%就可以了

-XX:G1MaxNewSizePercent 参数是用来设置新生代最大占比的,也不用设置 维持默认的60%就可以了

此时堆内存共4G,那么此时会除以2048,计算每个Region的大小,此时每个Region的大小就是2MB,刚开始新生代就占5%的Region,可以认为新生代就只有100个Region,有200MB的内存空间,如下图所示。


设置GC停顿时间

在G1垃圾回收器中有一个至关重要的参数会影响到GC的表现,就是 -XX:MaxGCPauseMills,它的默认值是200毫秒

也就是我们每次触发GC的时候导致的系统停顿时间 Stop the world 不要超过200ms,避免系统因为GC长时间卡死。

这个参数我们可以先保持一个默认值,继续往下分析看看,不着急忙下结论


到底多长时间会触发新生代GC?

有一个问题,就是系统运行起来之后,会不停的在新生代的Eden区域分配对象,按照之前的推算是每秒分配3MB的对象,如下图


那什么时候Eden的区域会不够呢?

前面我们说过 -XX:G1MAXNEWSIZE参数规定了新生代最大的堆内存空间

那么难道必须得随着系统运行一直给新生代分配更多得Region,直到新生代占据了60%之后,再进行GC?

G1肯定不是这样玩的

我们假设G1回收300Region需要 200ms

那么很有可能系统运行时,G1呈现出现如下的运行效果

首先,随着系统运行,每秒创建3MB的对象,大概1分钟左右就会塞满100个Region,如下图所示



此时很可能G1会觉得  ,要是我现在就触发GC,那么回收区区200MB 只需要大概几十ms,最多就让系统停止几十ms而已,跟我的主人设定的参数200Ms还相差很远。

如果现在gc, 那么每分钟都要GC会不会太频繁了,好像没有这个必要

所以还不如给新生代先增加一些Region,然后让系统继续运行再新生代Region中分配对象好了,这样就不用过于频繁触发新生代gc了,此时如图所示


然后系统继续进行,一直到可能300个Region都占满了,此时通过计算发现回收300个Region大概需要200ms,那么可能这个时候就会触发一次新生代gc了

G1是非常灵活的,它会根据你设置的时间 给新生代不停的分配更多Region

然后到一定程度,感觉差不多了,就会触发新生代gc,保证新生代Gc的时候导致的系统停顿时间再你预设的范围内


新生代如何优化

其实就是优化-XX:MaxGCPauseMills参数

如果这个参数设置小了 ,那么说明每次gc的停顿时间很短,但是很频繁

如果这个参数设置大了 停顿的时间就会非常长,

所以这个参数到底如何设置,需要结合工具来测试,来达到一个合理的值


老年代如何优化

其实也是核ParNew +CMS 控制不必要的对象进入老年代就好了,也是-XX:MaxGCPauseMills这个参数,

大家可以想象一下 如果这个参数很大,那么经过新生代的gc后,就会导致Survivor区域放不下那么多的对象,那么这些对象就会进入老年代了

或者是因为动态年龄判断进入老年代了,所以说还是设置这个参数


结尾


其实也就是把G1简单的说了一下,后面还有具体的干货

因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。

相关文章
|
3天前
|
监控 算法 Java
Java虚拟机(JVM)的垃圾回收机制深度解析####
本文深入探讨了Java虚拟机(JVM)的垃圾回收机制,旨在揭示其背后的工作原理与优化策略。我们将从垃圾回收的基本概念入手,逐步剖析标记-清除、复制算法、标记-整理等主流垃圾回收算法的原理与实现细节。通过对比不同算法的优缺点及适用场景,为开发者提供优化Java应用性能与内存管理的实践指南。 ####
|
1月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
65 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
1月前
|
存储 监控 算法
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程 ?
尼恩提示: G1垃圾回收 原理非常重要, 是面试的重点, 大家一定要好好掌握
美团面试:说说 G1垃圾回收 底层原理?说说你 JVM 调优的过程  ?
|
1月前
|
监控 架构师 Java
JVM进阶调优系列(6)一文详解JVM参数与大厂实战调优模板推荐
本文详述了JVM参数的分类及使用方法,包括标准参数、非标准参数和不稳定参数的定义及其应用场景。特别介绍了JVM调优中的关键参数,如堆内存、垃圾回收器和GC日志等配置,并提供了大厂生产环境中常用的调优模板,帮助开发者优化Java应用程序的性能。
|
1月前
|
存储 监控 算法
JVM调优深度剖析:内存模型、垃圾收集、工具与实战
【10月更文挑战第9天】在Java开发领域,Java虚拟机(JVM)的性能调优是构建高性能、高并发系统不可或缺的一部分。作为一名资深架构师,深入理解JVM的内存模型、垃圾收集机制、调优工具及其实现原理,对于提升系统的整体性能和稳定性至关重要。本文将深入探讨这些内容,并提供针对单机几十万并发系统的JVM调优策略和Java代码示例。
51 2
|
1月前
|
算法 Java
谈谈HotSpot JVM 中的不同垃圾回收器
【10月更文挑战第5天】理解 HotSpot JVM 中的不同垃圾回收器(如 CMS、G1 和 ZGC)的区别,需要深入了解它们的设计原理、工作方式和应用场景。以下是对这三个垃圾回收器的简要概述以及一个示例 Java 程序,虽然示例程序本身不能直接展示垃圾回收器的内部机制,但可以帮助观察不同垃圾回收器的行为。
27 1
|
1月前
|
存储 安全 Java
jvm 锁的 膨胀过程?锁内存怎么变化的
【10月更文挑战第3天】在Java虚拟机(JVM)中,`synchronized`关键字用于实现同步,确保多个线程在访问共享资源时的一致性和线程安全。JVM对`synchronized`进行了优化,以适应不同的竞争场景,这种优化主要体现在锁的膨胀过程,即从偏向锁到轻量级锁,再到重量级锁的转变。下面我们将详细介绍这一过程以及锁在内存中的变化。
37 4
|
12天前
|
Arthas 监控 Java
JVM进阶调优系列(9)大厂面试官:内存溢出几种?能否现场演示一下?| 面试就那点事
本文介绍了JVM内存溢出(OOM)的四种类型:堆内存、栈内存、元数据区和直接内存溢出。每种类型通过示例代码演示了如何触发OOM,并分析了其原因。文章还提供了如何使用JVM命令工具(如jmap、jhat、GCeasy、Arthas等)分析和定位内存溢出问题的方法。最后,强调了合理设置JVM参数和及时回收内存的重要性。
|
10天前
|
Java Linux Windows
JVM内存
首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制。
10 1
|
1月前
|
存储 缓存 算法
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!
JVM核心知识点整理(内存模型),收藏再看!