上星期知识星球的同学在阿里云技能面终面的时分被问到这么一个问题:假定一个每天100w次登陆恳求的平台,一个服务节点 8G 内存,该怎样设置JVM参数? 觉得答复的不太理想,过来找我复盘。

下面以面试题的形式给咱们梳理出来,做到一箭双雕:

  • 既供咱们实操参阅
  • 又供咱们面试参阅

咱们要学习的,除了 JVM 装备方案 之外,是其 分析问题的思路、思考问题的视角。 这些思路和视角,能帮助咱们走更远、更远。

接下来,进入正题。

文章首发大众号:码猿技能专栏

每天100w次登陆恳求, 8G 内存该怎样设置JVM参数?

每天100w次登陆恳求, 8G 内存该怎样设置JVM参数,大约能够分为以下8个进程

Step1:新体系上线怎样规划容量?

1.套路总结

任何新的事务体系在上线曾经都需求去预算服务器装备和JVM的内存参数,这个容量与资源规划并不仅仅是体系架构师的随意预算的,需求依据体系所在事务场景去预算,揣度出来一个体系运转模型,评价JVM功能和GC频率等等指标。以下是我结合大牛经历以及自身实践来总结出来的一个建模进程:

  • 核算事务体系每秒钟创立的方针会佔用多大的内存空间,然后核算集群下的每个体系每秒的内存佔用空间(方针创立速度)
  • 设置一个机器装备,预算新生代的空间,比较不同新生代巨细之下,多久触发一次MinorGC。
  • 为了防止频频GC,就能够从头预算需求多少机器装备,布置多少台机器,给JVM多大内存空间,新生代多大空间。
  • 依据这套装备,根本能够推算出整个体系的运转模型,每秒创立多少方针,1s今后成为废物,体系运转多久新生代会触发一次GC,频率多高。

2.套路实战——以登录体系为例

有些同学看到这些进程仍是发憷,说的好像是那么回事,一到实际项目中到底怎麽做我仍是不知道!

光说不练假把式,以登录体系为例模拟一下推演进程:

  • 假定每天100w次登陆恳求,登陆峰值在早上,预估峰值时期每秒100次登陆恳求。
  • 假定布置3台服务器,每台机器每秒处理30次登陆恳求,假定一个登陆恳求需求处理1秒钟,JVM新生代里每秒就要生成30个登陆方针,1s之后恳求结束这些方针成为了废物。
  • 一个登陆恳求方针假定20个字段,一个方针预算500字节,30个登陆佔用大约15kb,考虑到RPC和DB操作,网络通信、写库、写缓存一顿操作下来,能够扩大到20-50倍,大约1s发生几百k-1M数据。
  • 假定2C4G机器布置,分配2G堆内存,新生代则只要几百M,依照1s1M的废物发生速度,几百秒就会触发一次MinorGC了。
  • 假定4C8G机器布置,分配4G堆内存,新生代分配2G,如此需求几个小时才会触发一次MinorGC。

所以,能够粗略的揣度出来一个每天100w次恳求的登录体系,依照4C8G的3实例集群装备,分配4G堆内存、2G新生代的JVM,能够保障体系的一个正常负载。

根本上把一个新体系的资源评价了出来,所以搭建新体系要每个实例需求多少容量多少装备,集群装备多少个实例等等这些,并不是拍拍脑袋和胸脯就能够决定的下来的。

Step2:该怎样进行废物收回器的挑选?

吞吐量仍是呼应时刻

首要引入两个概念:吞吐量和低推迟

吞吐量 = CPU在用户运用程序运转的时刻 / (CPU在用户运用程序运转的时刻 + CPU废物收回的时刻)

呼应时刻 = 平均每次的GC的耗时

一般,吞吐优先仍是呼应优先这个在JVM中是一个两难之选。

堆内存增大,gc一次能处理的数量变大,吞吐量大;可是gc一次的时刻会变长,导致后面排队的线程等候时刻变长;相反,假如堆内存小,gc一次时刻短,排队等候的线程等候时刻变短,推迟削减,但一次恳求的数量变小(并不绝对契合)。

无法一起统筹,是吞吐优先仍是呼应优先,这是一个需求权衡的问题。

废物收回器设计上的考量

  • JVM在GC时不允许一边废物收回,一边还创立新方针(就像不能一边打扫卫生,还在一边扔废物)。
  • JVM需求一段Stop the world的暂停时刻,而STW会造成体系时刻短中止不能处理任何恳求;
  • 新生代搜集频率高,功能优先,常用仿制算法;老时代频次低,空间灵敏,防止仿制办法。
  • 一切废物收回器的触及方针都是要让GC频率更少,时刻更短,削减GC对体系影响!

CMS和G1

目前主流的废物收回器装备是新生代选用ParNew,老时代选用CMS组合的办法,或许是完全选用G1收回器,

从未来的趋势来看,G1是官方保护和更为推崇的废物收回器。

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

关注大众号:码猿技能专栏,回复要害词:1111 获取阿里内部Java功能调优手册!

事务体系:

  • 推迟灵敏的引荐CMS;
  • 大内存服务,要求高吞吐的,选用G1收回器!

CMS废物收回器的作业机制

CMS主要是针对老时代的收回器,老时代是符号-铲除,默许会在一次FullGC算法后做收拾算法,收拾内存碎片。

CMS GC 描绘 Stop the world 速度
1.开端符号 初始符号仅符号GCRoots能直接相关到的方针,速度很快 Yes 很快
2.并发符号 并发符号阶段便是进行GCRoots Tracing的进程 No
3.从头符号 从头符号阶段则是为了批改并发符号期间因用户程序继续运作而导致符号发生变化的那一部分方针的符号记载。 Yes 很快
4.废物收回 并发收拾废物方针(符号铲除算法) No
  • 长处:并发搜集、主打“低延时” 。在最耗时的两个阶段都没有发生STW,而需求STW的阶段都以很快速度完结。
  • 缺陷:1、消耗CPU;2、浮动废物;3、内存碎片
  • 适用场景:重视服务器呼应速度,要求体系中止时刻最短。

总之:

事务体系,推迟灵敏的引荐CMS;

大内存服务,要求高吞吐的,选用G1收回器!

Step3:怎样对各个分区的份额、巨细进行规划

一般的思路为:

首要,JVM最重要最中心的参数是去评价内存和分配,第一步需求指定堆内存的巨细,这个是体系上线有必要要做的,-Xms 初始堆巨细,-Xmx 最大堆巨细,后台Java服务中一般都指定为体系内存的一半,过大会佔用服务器的体系资源,过小则无法发挥JVM的最佳功能。

其次,需求指定-Xmn新生代的巨细,这个参数十分要害,灵敏度很大,尽管sun官方引荐为3/8巨细,可是要依据事务场景来定,针关于无状况或许轻状况服务(现在最常见的事务体系如Web运用)来说,一般新生代乃至能够给到堆内存的3/4巨细;而关于有状况服务(常见如IM服务、网关接入层等体系)新生代能够依照默许份额1/3来设置。服务有状况,则意味著会有更多的本地缓存和会话状况信息常驻内存,应为要给老时代设置更大的空间来寄存这些方针。

最终,是设置-Xss栈内存巨细,设置单个线程栈巨细,默许值和JDK版别、体系有关,一般默许512~1024kb。一个后台服务假如常驻线程有几百个,那麽栈内存这边也会佔用了几百M的巨细。

JVM参数 描绘 默许 引荐
-Xms Java堆内存的巨细 OS内存64/1 OS内存一半
-Xmx Java堆内存的最大巨细 OS内存4/1 OS内存一半
-Xmn Java堆内存中的新生代巨细,扣除新生代剩下的便是老时代的内存巨细了 跌认堆的1/3 sun引荐3/8
-Xss 每个线程的栈内存巨细 和idk有关 sun

关于8G内存,一般分配一半的最大内存就能够了,由于机器本上还要占用必定内存,一般是分配4G内存给JVM,

引入功能压测环节,测验同学对登录接口压至1s内60M的方针生成速度,选用ParNew+CMS的组合收回器,

正常的JVM参数装备如下:

-Xms3072M -Xmx3072M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -XX:SurvivorRatio=8

这样设置或许会由于动态方针年纪判别准则导致频频full gc。为啥呢?

压测进程中,短时刻(比方20S后)Eden区就满了,此时再运转的时分方针已经无法分配,会触发MinorGC,

假定在这次GC后S1装入100M,马上过20S又会触发一次MinorGC,多出来的100M存活方针+S1区的100M已经无法顺利放入到S2区,此时就会触发JVM的动态年纪机制,将一批100M左右的方针推到老时代保存,持续运转一段时刻,体系或许一个小时分内就会触发一次FullGC。

依照默许8:1:1的份额来分配时, survivor区只要 1G的 10%左右,也便是几十到100M,

假如 每次minor GC废物收回往后进入survivor方针许多,而且survivor方针巨细很快超越 Survivor 的 50% , 那么会触发动态年纪断定规矩,让部分方针进入老时代.

而一个GC进程中,或许部分WEB恳求未处理结束, 几十兆方针,进入survivor的概率,是十分大的,乃至是必定会发生的.

怎样处理这个问题呢? 为了让方针尽或许的在新生代的eden区和survivor区, 尽或许的让survivor区内存多一点,到达200兆左右,

于是咱们能够更新下JVM参数设置:

-Xms3072M -Xmx3072M -Xmn2048M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M  -XX:SurvivorRatio=8
阐明:
‐Xmn2048M ‐XX:SurvivorRatio=8 
年青代巨细2g,eden与survivor的份额为8:1:1,也便是1.6g:0.2g:0.2g

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

survivor到达200m,假如几十兆方针到底survivor, survivor 也不必定超越 50%

这样能够防止每次废物收回往后,survivor方针太早超越 50% ,

这样就下降了由于方针动态年纪判别准则导致的方针频频进入老时代的问题,

什么是JVM动态年纪判别规矩呢?

方针进入老时代的动态年纪判别规矩(动态提升年纪核算阈值):Minor GC 时,Survivor 中年纪 1 到 N 的方针巨细超越 Survivor 的 50% 时,则将大于等于年纪 N 的方针放入老时代。

中心的优化策略是:是让短期存活的方针尽量都留在survivor里,不要进入老时代,这样在minor gc的时分这些方针都会被收回,不会进到老时代然后导致full gc

应该怎样去评价新生代内存和分配适宜?

这儿特甭说一下,JVM最重要最中心的参数是去评价内存和分配,

第一步需求指定堆内存的巨细,这个是体系上线有必要要做的,-Xms 初始堆巨细,-Xmx 最大堆巨细,

后台Java服务中一般都指定为体系内存的一半,过大会佔用服务器的体系资源,过小则无法发挥JVM的最佳功能。

其次需求指定-Xmn新生代的巨细,这个参数十分要害,灵敏度很大,尽管sun官方引荐为3/8巨细,可是要依据事务场景来定:

  • 针关于无状况或许轻状况服务(现在最常见的事务体系如Web运用)来说,一般新生代乃至能够给到堆内存的3/4巨细;
  • 而关于有状况服务(常见如IM服务、网关接入层等体系)新生代能够依照默许份额1/3来设置。

服务有状况,则意味著会有更多的本地缓存和会话状况信息常驻内存,应为要给老时代设置更大的空间来寄存这些方针。

step4:栈内存巨细多少比较适宜?

-Xss栈内存巨细,设置单个线程栈巨细,默许值和JDK版别、体系有关,一般默许512~1024kb。一个后台服务假如常驻线程有几百个,那麽栈内存这边也会佔用了几百M的巨细。

step5:方针年纪应该为多少才移动到老时代比较适宜?

假定一次minor gc要间隔二三十秒,而且,大多数方针一般在几秒内就会变为废物,

假如方针这么长时刻都没被收回,比方2分钟没有收回,能够认为这些方针是会存活的比较长的方针,然后移动到老时代,而不是继续一向占用survivor区空间。

所以,能够将默许的15岁改小一点,比方改为5,

那么意味着方针要经过5次minor gc才会进入老时代,整个时刻也有一两分钟了(5*30s= 150s),和几秒的时刻比较,方针已经存活了足够长时刻了。

所以:能够适当调整JVM参数如下:

‐Xms3072M ‐Xmx3072M ‐Xmn2048M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:SurvivorRatio=8 ‐XX:MaxTenuringThreshold=5

step6:多大的方针,能够直接到老时代比较适宜?

关于多大的方针直接进入老时代(参数-XX:PretenureSizeThreshold),一般能够结合自己体系看下有没有什么大方针 生成,预估下大方针的巨细,一般来说设置为1M就差不多了,很少有超越1M的大方针,

所以:能够适当调整JVM参数如下:

‐Xms3072M ‐Xmx3072M ‐Xmn2048M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:SurvivorRatio=8 ‐XX:MaxTenuringThreshold=5 ‐XX:PretenureSizeThreshold=1M

step7:废物收回器CMS老时代的参数优化

JDK8默许的废物收回器是-XX:+UseParallelGC(年青代)和-XX:+UseParallelOldGC(老时代),

假如内存较大(超越4个G,仅仅经历 值),仍是主张运用G1.

这儿是4G以内,又是主打“低延时” 的事务体系,能够运用下面的组合:

ParNew+CMS(-XX:+UseParNewGC -XX:+UseConcMarkSweepGC)

新生代的选用ParNew收回器,作业流程便是经典仿制算法,在三块区中进行流转收回,只不过选用多线程并行的办法加快了MinorGC速度。

老生代的选用CMS。再去优化老时代参数:比方老时代默许在符号铲除今后会做收拾,还能够在CMS的添加GC频次仍是添加GC时长上做些取舍,

如下是呼应优先的参数调优:

XX:CMSInitiatingOccupancyFraction=70

设定CMS在对内存占用率到达70%的时分开端GC(由于CMS会有浮动废物,所以一般都较早发动GC)

XX:+UseCMSInitiatinpOccupancyOnly

和上面调配运用,否则只收效一次

-XX:+AlwaysPreTouch

强制操作体系把内存实在分配给IVM,而不是用时才分配。

综上,只要年青代参数设置合理,老时代CMS的参数设置根本都能够用默许值,如下所示:

‐Xms3072M ‐Xmx3072M ‐Xmn2048M ‐Xss1M ‐XX:MetaspaceSize=256M ‐XX:MaxMetaspaceSize=256M ‐XX:SurvivorRatio=8  ‐XX:MaxTenuringThreshold=5 ‐XX:PretenureSizeThreshold=1M ‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC ‐XX:CMSInitiatingOccupancyFraction=70 ‐XX:+UseCMSInitiatingOccupancyOnly ‐XX:+AlwaysPreTouch

参数解释

1.‐Xms3072M ‐Xmx3072M 最小最大堆设置为3g,最大最小设置为共同防止内存抖动

2.‐Xss1M 线程栈1m

3.‐Xmn2048M ‐XX:SurvivorRatio=8 年青代巨细2g,eden与survivor的份额为8:1:1,也便是1.6g:0.2g:0.2g

4.-XX:MaxTenuringThreshold=5 年纪为5进入老时代 5.‐XX:PretenureSizeThreshold=1M 大于1m的大方针直接在老时代生成

6.‐XX:+UseParNewGC ‐XX:+UseConcMarkSweepGC 运用ParNew+cms废物收回器组合

7.‐XX:CMSInitiatingOccupancyFraction=70 老时代中方针到达这个份额后触发fullgc

8.‐XX:+UseCMSInitiatinpOccupancyOnly 老时代中方针到达这个份额后触发fullgc,每次

9.‐XX:+AlwaysPreTouch 强制操作体系把内存实在分配给IVM,而不是用时才分配。

step8:装备OOM时分的内存dump文件和GC日志

额定添加了GC日志打印、OOM主动dump等装备内容,帮助进行问题排查

-XX:+HeapDumpOnOutOfMemoryError

在Out Of Memory,JVM快死掉的时分,输出Heap Dump到指定文件。

否则开发许多时分还真不知道怎样重现过错。

路径只指向目录,JVM会坚持文件名的唯一性,叫java_pid${pid}.hprof。

-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=${LOGDIR}/

由于假如指向特定的文件,而文件已存在,反而不能写入。

输出4G的HeapDump,会导致IO功能问题,在一般硬盘上,会造成20秒以上的硬盘IO跑满,

需求留意一下,但在容器环境下,这个也会影响同一宿主机上的其他容器。

GC的日志的输出也很重要:

-Xloggc:/dev/xxx/gc.log
-XX:+PrintGCDateStamps 
-XX:+PrintGCDetails

GC的日志实际上对体系功能影响不大,打日志对排查GC问题很重要。

一份通用的JVM参数模板

一般来说,大企业或许架构师团队,都会为项目的事务体系定制一份较为通用的JVM参数模板,可是许多小企业和团队或许就疏于这一块的设计,假如老板某一天忽然让你负责定制一个新体系的JVM参数,你上网去搜许多的JVM调优文章或博客,结果发现都是零零散散的、不成体系的JVM参数解说,根本下不了手,这个时分你就需求一份较为通用的JVM参数模板了,不能确保功能最佳,可是至少能让JVM这一层是安稳可控的,

在这儿给咱们总结了一份模板:

依据4C8G体系的ParNew+CMS收回器模板(呼应优先),新生代巨细依据事务灵敏调整!

-Xms4g
-Xmx4g
-Xmn2g
-Xss1m
-XX:SurvivorRatio=8
-XX:MaxTenuringThreshold=10
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=70
-XX:+UseCMSInitiatingOccupancyOnly
-XX:+AlwaysPreTouch
-XX:+HeapDumpOnOutOfMemoryError
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:gc.log

假如是GC的吞吐优先,引荐运用G1,依据8C16G体系的G1收回器模板:

G1搜集器自身已经有一套预测和调整机制了,因而咱们首要的挑选是信任它,

即调整-XX:MaxGCPauseMillis=N参数,这也契合G1的目的——让GC调优尽量简略!

一起也不要自己显式设置新生代的巨细(用-Xmn或-XX:NewRatio参数),

假如人为干涉新生代的巨细,会导致方针时刻这个参数失效。

-Xms8g
-Xmx8g
-Xss1m
-XX:+UseG1GC
-XX:MaxGCPauseMillis=150
-XX:InitiatingHeapOccupancyPercent=40
-XX:+HeapDumpOnOutOfMemoryError
-verbose:gc
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-Xloggc:gc.log
G1参数 描绘 默许值
XX:MaxGCPauseMillis=N 最大GC中止时刻。柔性方针,JVM满意90%,不确保100%。 200
-XX:nitiatingHeapOccupancyPercent=n 当整个堆的空间运用百分比超越这个值时,就会融发MixGC 45

针对-XX:MaxGCPauseMillis来说,参数的设置带有明显的倾向性:调低↓:推迟更低,但MinorGC频频,MixGC收回老时代区削减,增大Full GC的危险。调高↑:单次收回更多的方针,但体系整体呼应时刻也会被拉长。

针对InitiatingHeapOccupancyPercent来说,调参巨细的效果也不相同:调低↓:更早触发MixGC,浪费cpu。调高↑:堆积过多代收回region,增大FullGC的危险。

调优总结

体系在上线前的综合调优思路:

1、事务预估:依据预期的并发量、平均每个使命的内存需求巨细,然后评价需求几台机器来承载,每台机器需求什么样的装备。

2、容量预估:依据体系的使命处理速度,然后合理分配Eden、Surivior区巨细,老时代的内存巨细。

3、收回器选型:呼应优先的体系,主张选用ParNew+CMS收回器;吞吐优先、多核大内存(heap size≥8G)服务,主张选用G1收回器。

4、优化思路:让短寿方针在MinorGC阶段就被收回(一起收回后的存活方针<Survivor区域50%,可操控保留在新生代),龟龄方针尽早进入老时代,不要在新生代来回仿制;尽量削减Full GC的频率,防止FGC体系的影响。

5、到目前为止,总结到的调优的进程主要依据上线前的测验验证阶段,所以咱们尽量在上线之前,就将机器的JVM参数设置到最优!

JVM调优仅仅一个手法,但并不必定一切问题都能够经过JVM进行调优处理,大多数的Java运用不需求进行JVM优化,咱们能够遵循以下的一些准则:

  • 上线之前,应先考虑将机器的JVM参数设置到最优;
  • 削减创立方针的数量(代码层面);
  • 削减运用全局变量和大方针(代码层面);
  • 优先架构调优和代码调优,JVM优化是不得已的手法(代码、架构层面);
  • 分析GC情况优化代码比优化JVM参数更好(代码层面);

经过以上准则,咱们发现,其实最有效的优化手法是架构和代码层面的优化,而JVM优化则是最终不得已的手法,也能够说是对服务器装备的最终一次“压榨”。

什么是ZGC?

ZGC (Z Garbage Collector)是一款由Oracle公司研发的,以低推迟为首要方针的一款废物搜集器。

它是依据动态Region内存布局,(暂时)不设年纪分代,运用了读屏障、染色指针和内存多重映射等技能来完结可并发的符号-收拾算法的搜集器。

在 JDK 11 新参加,还在试验阶段,

主要特点是:收回TB级内存(最大4T),中止时刻不超越10ms。

长处:低中止,高吞吐量, ZGC 搜集进程中额定消耗的内存小

缺陷:浮动废物

目前运用的十分少,实在普及仍是需求写时刻的。

怎样挑选废物搜集器?

在实在场景中应该怎样去挑选呢,下面给出几种主张,期望对你有帮助:

1、假如你的堆巨细不是很大(比方 100MB ),挑选串行搜集器一般是效率最高的。参数:-XX:+UseSerialGC

2、假如你的运用运转在单核的机器上,或许你的虚拟机核数只要 单核,挑选串行搜集器依然是适宜的,这时分启用一些并行搜集器没有任何收益。参数:-XX:+UseSerialGC

3、假如你的运用是“吞吐量”优先的,而且对较长时刻的中止没有什么特别的要求。挑选并行搜集器是比较好的。参数:-XX:+UseParallelGC

4、假如你的运用对呼应时刻要求较高,想要较少的中止。乃至 1 秒的中止都会引起许多的恳求失败,那么挑选 G1 、 ZGC 、 CMS 都是合理的。尽管这些搜集器的 GC 中止一般都比较短,但它需求一些额定的资源去处理这些作业,一般吞吐量会低一些。参数:-XX:+UseConcMarkSweepGC-XX:+UseG1GC-XX:+UseZGC 等。从上面这些出发点来看,咱们往常的 Web 服务器,都是对呼应性要求十分高的。

挑选性其实就集中在 CMS、G1、ZGC 上。而关于某些定时使命,运用并行搜集器,是一个比较好的挑选。

Hotspot为什么运用元空间替换了永久代?

什么是元空间?什么是永久代?为什么用元空间代替永久代?

咱们先回顾一下办法区吧,看看虚拟机运转时数据内存图,如下:

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

办法区和堆相同,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

什么是永久代?它和办法区有什么关系呢?

假如在HotSpot虚拟机上开发、布置,许多程序员都把办法区称作永久代。

能够说办法区是规范,永久代是Hotspot针对该规范进行的完结。

在Java7及曾经的版别,办法区都是永久代完结的。

什么是元空间?它和办法区有什么关系呢?

关于Java8,HotSpots取消了永久代,取而代之的是元空间(Metaspace)。

换句话说,便是办法区仍是在的,仅仅完结变了,从永久代变为元空间了。

为什么运用元空间替换了永久代?

永久代的办法区,和堆运用的物理内存是接连的。

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

永久代是经过以下这两个参数装备巨细的~

  • -XX:PremSize:设置永久代的初始巨细
  • -XX:MaxPermSize: 设置永久代的最大值,默许是64M

关于永久代,假如动态生成许多class的话,就很或许呈现java.lang.OutOfMemoryError:PermGen space过错,由于永久代空间装备有限嘛。最典型的场景是,在web开发比较多jsp页面的时分。

JDK8之后,办法区存在于元空间(Metaspace)。

物理内存不再与堆接连,而是直接存在于本地内存中,理论上机器内存有多大,元空间就有多大

每天100w次登陆请求, 8G 内存该如何设置JVM参数?

能够经过以下的参数来设置元空间的巨细:

  • -XX:MetaspaceSize,初始空间巨细,到达该值就会触发废物搜集进行类型卸载,一起GC会对该值进行调整:假如释放了许多的空间,就适当下降该值;假如释放了很少的空间,那么在不超越MaxMetaspaceSize时,适当提高该值。
  • -XX:MaxMetaspaceSize,最大空间,默许是没有限制的。
  • -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩下空间容量的百分比,削减为分配空间所导致的废物搜集
  • -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩下空间容量的百分比,削减为释放空间所导致的废物搜集

所以,为什么运用元空间替换永久代?

表面上看是为了防止OOM反常。

由于一般运用PermSize和MaxPermSize设置永久代的巨细就决定了永久代的上限,可是不是总能知道应该设置为多大适宜, 假如运用默许值很容易遇到OOM过错。

当运用元空间时,能够加载多少类的元数据就不再由MaxPermSize操控, 而由体系的实际可用空间来操控啦。

什么是Stop The World ? 什么是OopMap?什么是安全点?

进行废物收回的进程中,会触及方针的移动。

为了确保方针引用更新的正确性,有必要暂停一切的用户线程,像这样的中止,虚拟机设计者形象描绘为Stop The World。也简称为STW。

在HotSpot中,有个数据结构(映射表)称为OopMap

一旦类加载动作完结的时分,HotSpot就会把方针内什么偏移量上是什么类型的数据核算出来,记载到OopMap。

在即时编译进程中,也会在特定的方位生成 OopMap,记载下栈上和寄存器里哪些方位是引用。

这些特定的方位主要在:1.循环的结尾(非 counted 循环)

2.办法临回来前 / 调用办法的call指令后

3.或许抛反常的方位

这些方位就叫作安全点(safepoint)。

用户程序履行时并非在代码指令流的恣意方位都能够在中止下来开端废物搜集,而是有必要是履行到安全点才能够暂停。