大家好,我是不才陈某~
上周常识星球的同学在阿里云技术面终面的时候被问到这么一个问题:假如一个每天100w次登陆申请的平台,一个服务节点 8G 内存,该如何设置JVM参数? 感觉答复的不太现实,过去找我复盘。
上面以面试题的模式给大家梳理进去,做到一举两得:
- 既供大家实操参考
- 又供大家面试参考
大家要学习的,除了 JVM 配置计划 之外,是其 剖析问题的思路、思考问题的视角。 这些思路和视角,能帮忙大家走更远、更远。
接下来,进入正题。
关注公众号:码猿技术专栏,回复关键词:1111 获取阿里外部Java性能调优手册!
每天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是官网保护和更为推崇的垃圾回收器。
业务零碎:
- 提早敏感的举荐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
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为什么应用元空间替换了永恒代?
什么是元空间?什么是永恒代?为什么用元空间代替永恒代?
咱们先回顾一下办法区吧,看看虚拟机运行时数据内存图,如下:
办法区和堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、动态变量、即时编译后的代码等数据。
什么是永恒代?它和办法区有什么关系呢?
如果在HotSpot虚拟机上开发、部署,很多程序员都把办法区称作永恒代。
能够说办法区是标准,永恒代是Hotspot针对该标准进行的实现。
在Java7及以前的版本,办法区都是永恒代实现的。
什么是元空间?它和办法区有什么关系呢?
对于Java8,HotSpots勾销了永恒代,取而代之的是元空间(Metaspace)。
换句话说,就是办法区还是在的,只是实现变了,从永恒代变为元空间了。
为什么应用元空间替换了永恒代?
永恒代的办法区,和堆应用的物理内存是间断的。
永恒代是通过以下这两个参数配置大小的~
-XX:PremSize
:设置永恒代的初始大小-XX:MaxPermSize
: 设置永恒代的最大值,默认是64M
对于永恒代,如果动静生成很多class的话,就很可能呈现java.lang.OutOfMemoryError:PermGen space谬误,因为永恒代空间配置无限嘛。最典型的场景是,在web开发比拟多jsp页面的时候。
JDK8之后,办法区存在于元空间(Metaspace)。
物理内存不再与堆间断,而是间接存在于本地内存中,实践上机器内存有多大,元空间就有多大。
能够通过以下的参数来设置元空间的大小:
-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)。
用户程序执行时并非在代码指令流的任意地位都可能在停顿下来开始垃圾收集,而是必须是执行到平安点才可能暂停。
最初说一句(别白嫖,求关注)
陈某每一篇文章都是精心输入,如果这篇文章对你有所帮忙,或者有所启发的话,帮忙点赞、在看、转发、珍藏,你的反对就是我坚持下去的最大能源!
关注公众号:【码猿技术专栏】,公众号内有超赞的粉丝福利,回复:加群,能够退出技术探讨群,和大家一起探讨技术,吹牛逼!