目录
3.3 Eden区与Survivor区的空间大小壁纸为什么默认是8:1:1?
3.5.3 Paraller Scavenge收集器(复制)
3.1 堆为什么要分成年轻代和老年代?
因为年轻代和老年代的特点不同,需要用不同的垃圾回收算法;
老年代的对象,他的特点是需要长期存活,所以需要另外一种垃圾回收算法;
所以需要分为两个区域去存放不同的对象;
1、绝大多数对象都是朝生夕死的;
如果一个区域里面的对象大多都是朝生夕死的,你们就把他们集中在一起,每次回收的时候只关注如何保留少量存活对象,而不是去标记哪些大量将要被回收的对象,就能以较低的待机回收大量的空间;
2、熬过了很多次垃圾收集的对象就越难回收;
如果是需要长期存活的对象,就把他们集中放在一起,虚拟机就可以使用较低的频率来回收这个区域,这样就兼顾了垃圾收集的时间开销和内存的空间有效利用;
3、JVM划分出新生代和老年代之后,垃圾收集器可以每次只回收其中某一个部分或者某些部分的区域,同时有了 Minor GC Major GC Full GC这样的回收类型划分;
Minor GC:新生代收集
Major GC:老年代收集、
Full GC:整堆收集:收集新生代、老年代以及元空间(方法区)的垃圾收集;
Mixed GC:混合收集,收集整个新生代及部分老年代的垃圾收集,目前只有G1收集器会有这种行为;
4、根据不同区域对象的存亡特征采用不同的垃圾收集算法:
标记-复制算法
标记-清除算法
标记-整理算法
3.2 JVM堆的年轻代为什么要有两个Survivor区?
1、如果没有Survivor区
这个时候当Eden区域满了之后,每出发一次Minor GC就会把Eden区的对象复制到老年代,这样当老年代满了之后就会出发Major GC\Full GC,频繁出发Full GC会影响性能;
2、如果只有一个Survivor区
创建的对象在Eden中,一旦Eden满了,出发一次Minor GC,Eden中存活的对象就会被移动到Survivor区,下一次Eden满了之后,此时进行Minor GC,Eden和Survivor区域就会将存活的对象存放到老年区,但是这样的垃圾清除会导致Survivor区域会出现内存碎片的问题,当下一次Eden中有一个大对象的时候,可能导致没有一个连续的空间存放,就会触发Minor GC,影响性能;救国不想出现内存碎片还可以进行标记-整理算法,但是标记整理算法又会耗费开销,影响性能;
3.3 Eden区与Survivor区的空间大小壁纸为什么默认是8:1:1?
3.4 请介绍JVM中的垃圾回收算法?
1、 标记-清除算法
1、标记
标记处所需要回收的对象在,在标记完成之后,统一回收掉所有被标记的对象,也可以反过来,标记处所有存活的对象,在标记完成之后,统一回收所有未被标记的对象,标记过程就是判断对象是否属于垃圾的过程,基于可达性分析算法判断对象是否可以回收;
2、清除
对被标记为垃圾的对象进行回收;
优点:
基于可达性分析算法,算法实现简单,后续收集算法都是基于这种思想实现的;
缺点:
1、执行效率不稳定,如果java堆中包含大量对象,而且其中大部分是需要被回收的,这是就必须进行大量标记和清除的动作,导致标记和清除两个过程的执行效率都随对象数量增长而降低;
2、内存空间的碎片化问题,标记、清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当以后再程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发一次垃圾收集;
2、复制算法 (新生代)
将内存按容量分为大小相等的两块,每次只是用其中一块,当这一块内存用完了,就将还存活的对象复制到另外一块内存上,然后把已使用过的内存空间一次清理掉;
优点:实现简单,效率高,解决了标记-清除算法导致的内存碎片问题;
缺点:
1、代价太大,将可分配内存缩小了一半,空间浪费太多;
2、对象存活率较高时,就要进行较多的复制操作,效率将会降低;(所以一般适用于对象存活率低的地方,也就是新生代)
一般虚拟机都会采用该算法来回收新生代,但是JVM对复制算法进行改进,JVM并没有按照1:1的比例来划分新生代的内存空间,因为通过大量的统计和研究表明,90%的对象是需要被回收的;
就会把需要回收的对象复制到另外一块Survivor上面;
这样就只有10%的空间被浪费;这就是优化的地方
当然90%的对象可以被回收是大部分情况下,我们不能保证每次回收都只有不多于10%的对象存活,因此JVM还有一个空间担保机制,当Survivor空间不足以容纳一次Minor GC之后存活的对象时,就需要依赖其他内存区域(老年代)进行空间分配担保(也就是冒险Minor GC);
3、标记整理算法(根据老年代的特点产生的)
1、标记
标记过程和标记清除算法一致,也是基于可达性分析算法进行标记;
2、整理
和标记清除不同,该算法不是针对可回收对象进行清除,而是根据存活对象进行整理,让存活对象都向一端移动,然后直接清理掉边界以外的内存;
标记清除算法不移动存活对象,导致有大量不连续空间,也就是内存碎片,老年代这种每次都有大量存活对象的区域,移动存活对象并更新所有引用这些对象引用,这是一种比较耗时的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,这种停顿称为:Stop The World :STW;
但是即便移动存活对象是非常耗时的操作,但是如果不那么做,我们在充满内存碎片空间中分配对象,又影响了对象的分配和访问效率,所以JVM再权衡两者之后,还是采用了移动存活对象得到方式,也就是对内存进行整理;
优点:
不会像复制算法划分两个区域,提高空间利用率,不会产生内存碎片
缺点:
效率低,除了像标记清除算法一样进行标记之外,还多了一步整理的过程,效率变低;
补充:
CMS垃圾收集器:平时多数时间都采用标记清除算法,暂时容忍内存碎片的存放,直到内存空间的碎片化程度已经大到影响对象分配时,再采用标记-整理算法收集一次,获得规整的内存空间,所以像基于标记-清除算法的CMS收集器面临空间碎片过多时就会进行一次整理;
4、分代收集器
根据对象存活的周期不同将内存划分为几块,一般java分为新生代和老年代,JVM根据各个年代的特点采用不同的收集算法;
新生代中:
每次进行垃圾回收都会发现大量对象死去,只有少量存活,因此少量存活,因此需要采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;
老年代:
老年代中对象存活率较高,采用标记清除、标记整理算法进行回收;
3.5 请介绍一下JVM垃圾收集器
新生代收集器:Serial、ParNew、Parller Scavenge;
老年代收集器:CMS、Serial Old、Paraallel Old;
整堆收集器:G1;
垃圾收集器的最前沿成果:ZGC和Shenandoah;
3.5.1 Serial 收集器
新生代收集器、最早的收集器,单线程的,收集时需暂停用户线程的工作,有卡顿现象;
Serial收集器简单,不会有线程切换的开销,是Client模式下默认的垃圾收集器,-client,-server;
单线程收集器
3.5.2 ParNew 收集器
新生代收集器,就是Serial收集器的多线程版本,大部分基本一样,单CPU下,ParNew还需要切换线程,可能还不如Serial;
Serial、ParNew收集器可以配合CMS收集器,前者收集新生代,后者CMS收集老年代;
指定使用CMS后,会默认使用ParNew作为新生代垃圾收集器;
3.5.3 Paraller Scavenge收集器(复制)
新生代收集器,基于复制算法,并行的多线程收集器和ParNew类似,
侧重于达到一个可控的吞吐量,虚拟机运行100分钟,垃圾收集1分钟,吞吐量99%,也称该垃圾收集器为吞吐量垃圾收集器,或者吞吐量优先的垃圾收集器;
这个垃圾收集器是jvm默认的新生代垃圾收集器;
-XX:MaxGCPauseMillis:该参数可以设置每次GC的时间尽量不要超过这个值,但是这个值并不是越小越好,因为GC时间短了,那么GC的次数就会变得更多;
Parallel Scavenge区别于ParNew的一个重要特征就是:
-XX:+UseAdaptiveSizePolicy 自适应新生代大小策略(默认开启),这个参数可以自动调整新生代、E、S区比例、进入老年代对象大小等参数,JVM虚拟机会根据当前系统的运行情况进行监控,动态调整这些参数,来提供最合适的停顿时间获得最大的吞吐量,这种调节方式称为垃圾收集的自适应的调节策略;
自动调整GC暂停时间和吞吐量之间的平衡,自动化虚拟机的性能;
3.5.4 Serial Old收集器(标记整理)
它是Serial收集器的老年代版本和Serial一样,单线程,可在Client模式下使用,也可以在Server模式下使用,采用标记-整理算法,Serial Old收集器也可以作为CMS收集器发生失败时的预备方案,在并发收集发生Concurrent Mode Failure的时候使用;
3.5.5 Paraller Old收集器(标记整理)
是Parallel Scavenge的老年代版本,多线程,标记整理算法,jdk1.6开始提供;
在注重吞吐量和CPU资源的情况下,Parallel Scavenge新生代+Paraller Old老年代是一个很好的搭配;
参数:-XX:+UseParallerOldGC指定Parallel Old收集器
JDK 1.8 默认情况下的垃圾收集器就是Paraller Scavenge收集器和Paraller Old收集器;
3.5.6 CMS收集器(标记清除、STW时间短)
CMS:Concurrent Mark Sweep(并发的),是一款老年代的垃圾收集器,他是追求最短回收停顿时间为目标的收集器,互联网B/S结构的服务器端特别适合此收集器;
垃圾回收会带来Stop the World的问题,会导致系统卡死时间过长,很多响应无法啊处理,所以CMS垃圾回收器采用的是垃圾回收线程和系统工作线程尽量同时执行的模式来处理,基于标记-清除算法;
通过参数:-XXUseConcurrentMarkSweep指定使用CMS垃圾收集器;当设置老年代的收集器是CMS之后,新生代就是ParNewGC;
CMS垃圾收集器的运作过程
分为4个阶段:
1、初始标记(stw,标记一下GC Roots能直接关联到的对象,那么这些对象也就是需要存活的对象,速度很快);
2、并发标记(不会stw,追踪GC Roots的整个链路,从GC Roots的直接关联对象开始遍历整个对象引用链路,这个过程耗时较长但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行);用户一边运行,一边标记对象
3、重新标记(stw,修正并发标记期间,因用户程序继续运行而导致产生变化的那一部分对象的标记记录,这个阶段的停顿时间通常会初始标记阶段稍长一些,单也远比并发标记的时间短,他其实就是堆在第二阶段中被系统运行变动过的少数对象进行标记,所以运行速度很快);
4、并发清除(不会stw,清除删除掉标记阶段判断的已经死亡的对象,这个阶段其实是很耗时的,但是由于不需要移动存活对象,并且这个阶段也是与用户线程同时并发执行的);
总结:其中初始标记和重新标记需要暂停用户线程(STW),其他阶段都是并发执行,所以总体上暂停时间更短;
CMS垃圾收集器的缺点
1、并发收集占用CPU资源,占用用户线程,导致用户线程的性能下降,CMS默认启动的回收线程数是(CPU核心数量+3)/4;
2、会产生浮动垃圾,因为并发清除的时候用户线程可能还在产生垃圾,这些垃圾没有被清除,不能让老年代填满之后再清除(CMS是处理老年代的,当老年代满了之后,用户线程就会停止,就不能保证用户线程和垃圾收集线程并发执行),需要给用户线程留有一定的空间,jdk5默认68%触发回收,jdk92%触发回收;
如果预留给用户线程的空间不够就会并发清除失败,启用Serial Old收集,这个时候就会暂停用户线程,导致性能下降;
3、CMS基于标记清除算法,清理之后会产生碎片空间,空间碎片过多的时候,将会导致大对象无法分配,往往会出现老年代还有很多剩余空间,但是没有足够大的连续空间来分配当前对象,这个时候就会提前触发Full GC;
第一个参数设置是否开启碎片整理;默认开启
第二个参数设置多少次之后进行碎片整理;默认开启,默认值为0每次都会整理;
3.5.7 G1收集器(新生代、老年代都是复制)
JDK 8的时候默认垃圾收集器为:Parallel Scavenge 和 Parallel Old组合;
JDK 9 默认垃圾收集器为:G1;并且在9的时候CMS就被声明为:不推荐使用的垃圾收集器,如果在9的环境下使用:-XX:+UseConcMarkSweepGC来开启CMS收集器就会出现一个警告,提示用户该收集器将来会被淘汰;
3.5.7.1 G1垃圾收集器的基本原理
G1是一款可以让我们设置垃圾回收的预期停顿时间的垃圾收集器;设置参数是-XX:MaxGCPauseMillis,默认值是200ms;时间太短,垃圾收集时间少,每次收集的垃圾就不多,就导致频繁的垃圾收集;
设置之后不管垃圾多少,垃圾回收的时间都不要超过我们设置的值,这就相当于我们直接控制垃圾回收对系统性能的影响;
G1垃圾收集器尽量把垃圾回收对系统造成的影响控制在指定的范围内,同时在优先的时间内尽量回收尽可能多的垃圾对象,这就是G1垃圾收集器的核心原理;
3.5.7.2 G1垃圾收集器如何做到可预测的停顿时间?
1、这与G1垃圾收集器独特的设计有关,它最大的特点就是把Java堆内存拆分为多个大小相等Redion;
2、G1他会追踪每个Region的回收价值,计算每个Region里面有多少对象是垃圾,对这个区域进行回收需要多长时间;
3、G1之所以可以建立可预测的停顿时间模型,因为他把Region作为了垃圾回收的最小单元,这样就可以每次只回收一部分的Region,避免了对堆进行全区域的垃圾回收,让G1收集器去追踪每一个Rgeion中的回收价值,然后在后台去维护一个优先级列表,根据用户设置的时间,去优先处理回收价值大的Region,这也是”Garbage First”名字的由来,这种使用Region划分堆内存空间,基于回收价值的回收方式,保证了G1收集器在有限的时间内尽可能的收集更多的垃圾;
3.5.7.3 G1垃圾收集器中的大对象
3.5.7.4 G1垃圾收集器内存大小如何设置?
-XX:G1HeapRegionSize来设置每个Region的大小;
默认新生代对堆内存的占比是5%,可以通过-XX:G1NewSizePercent来设置新生代初始占比;
新生代对打不会超过60%,可以通过-XX:G1MaxNewSizePercent设置,当进行了垃圾回收之后,新生代的Region就会变少,是一个动态的;
新生代 : 老年代 = 60 % : 40%;
Eden在变化,S区也在变化,始终满足8:1:1;
3.5.7.5 G1垃圾收集器的新生代垃圾回收(复制)
随着不断的在新生代的Eden区中增加对象,JVM就会不断的给新生代添加更多的Region,直到新生代占据堆大小的60%;
新生代就会触发GC,G1采用的依然是复制算法来进行垃圾回收,进入一个STW的状态,然后把Region中 的存活对象复制到S0对应的Region中,然后回收Eden中的垃圾对象;
区别:G1垃圾收集器可以设置停顿时间,G1会对每个Region追踪,计算回收它需要的时间和能回收多少对象(回收价值),根据用户设置的时间来选择性的回收一部分Region,保证G1在垃圾收集的时候停顿时间在指定范围内,尽可能的回收对象;
3.5.7.6 G1垃圾收集器的老生代垃圾回收
总结:
G1垃圾收集器除了并发标记外,其余阶段要完全暂停用户线程,所以他并不是纯粹的追求低延迟,而是给他设定暂停目标,使其在延迟可控的情况下获得尽可能高的吞吐量;
3.5.7.7 G1垃圾收集器的混合垃圾回收
混合垃圾收集即mixed gc,他不是一个old gc,除了回收整个young region,还回收一部分的old region,是回收一部分老年代,而不是全部老年代;可以选择部分old region进行收集,从而可以对垃圾回收的耗时时间进行控制;
3.5.7.8 G1回收失败时的Full GC
在进行mixed回收的时候,新生代和老年代都是采用复制算法进行回收,把Region中存活的对象复制到空闲的Rgeion中,如果出现复制的时候没有空闲的Region可以存放独享,就会停止系统程序吗,然后采用单线程进行标记清理和压缩整理,空闲出一批Region;
与CMS中的”Concurrent Mode Failure“失败会导致Full GC类似,如果内存回收的速度赶不上内存分配的速度,G1收集器就会暂停用户进程,导致Full GC产生长时间Stop The World;
今天的文章JVM-03:JVM垃圾收集器面试题分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/60481.html