JVM参数、GC

参数

-Xms设置最小堆空间大小(一般建议和-Xmx一样)。
-Xmx设置最大堆空间大小。
-Xmn设置新生代大小。
-XX:MetaspaceSize设置最小元数据空间大小。
-XX:MaxMetaspaceSize设置最大元数据空间大小。
-Xss设置每个线程的堆栈大小

参数-XX:SurvivorRatio用来表示s0、s1、eden之间的比例,默认情况下-XX:SurvivorRatio=8表示 s0:s1:eden=1:1:8

-XX:+UseCMSInitiatingOccupancyOnly
始终基于设定的阈值,不根据运行情况进行调整。

如果没有 -XX:+UseCMSInitiatingOccupancyOnly 这个参数, 只有第一次会使用CMSInitiatingPermOccupancyFraction=65 这个值. 后面的情况会自动调整。
我们用-XX+UseCMSInitiatingOccupancyOnly标志来命令JVM不基于运行时收集的数据来启动CMS垃圾收集周期。而是,当该标志被开启时,JVM通过CMSInitiatingOccupancyFraction的值进行每一次CMS收集,而不仅仅是第一次。然而,请记住大多数情况下,JVM比我们自己能作出更好的垃圾收集决策。因此,只有当我们充足的理由(比如测试)并且对应用程序产生的对象的生命周期有深刻的认知时,才应该使用该标志。
推荐:jvm参数陷阱 - code-craft - SegmentFault 思否

JVM垃圾回收算法

  • 依据对象的存活周期进行分为新生代,老年代。
  • 根据不同代的特点,选取合适的收集算法
    • 新生代,适合复制算法
    • 老年代,适合标记清理或者标记压缩(也称标记整理)

复制算法

不适用于存活对象较多的场合 如老年代。(年轻代对象基本都是朝生夕灭所以特别适合,由于那样的话复制就少,如果类似老年代有大量存活对象,那么进行复制算法性能就不是特别好了)

标记清除

标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。在标记阶段,首先先找出根对象,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。

CMS收集器是基于“标记-清除”算法实现的

标记压缩 (也称标记整理)

标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记。但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。

http://www.jiangxinlingdu.com/jvm/2017/11/26/jvm-analyze.html

GC收集器

除了Serial外,目前只有ParNew能与CMS进行配合。
ParNew也是使用-XX:+UseConcMarkSweepGC后默认的新生代收集器。

CMS

  • 初始标记(CMS initial mark)STW
    只是标记一下GC Roots能直接关联到的对象,速度很快
  • 并发标记(CMS concurrent mark)
    进行GC Roots Tracing的过程
  • 重新标记(CMS remark)STW
    为了修正并发标记期间,因用户线程继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短
  • 并发清除(CMS concurrent sweep)

整个过程中耗时最长的是并发标记并发清除

CMS缺点:

  • CMS对CPU资源非常敏感。CMS默认启动的回收线程数是(CPU数量+3)/4
  • CMS无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次Full GC产生。浮动垃圾并发清理阶段的用户线程产生的垃圾;因此CMS不能像其他收集器那样等到老年代几乎完全被填满了再收集,需要预留一部分空间提供收集时用户线程使用。默认下,CMS在老年代到达使用阈值(默认值jdk1.5之前是68%,jdk1.6之后是92%)的空间就会激活,可以通过-XX:CMSInitiatingOccupancyFraction 和 -XX:+UseCMSInitiatingOccupancyOnly来设置。如果CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”,这时启动后备预案:临时启用Serial Old进行老年代收集。
  • CMS是基于标记-清除,会产生大量碎片而导致大对象分配出现问题,不得不提前触发一次Full GC。可以通过-XX:+UseCMSCompactAtFullCollection在Full GC后再附加一次碎片整理;也可以通过-XX:CMSFullGCsBeforeCompaction设置多少次不压缩的Full GC后,来一次带压缩的Full GC

《深入理解Java虚拟机:JVM高级特性与最佳实践》3.4

图解GC过程:HotSpot虚拟机中垃圾收集器的介绍 -

更详细的CMS

不可错过的CMS学习笔记 | 并发编程网 – ifeve.com

  • CMS中minor gc和major gc是顺序发生的吗?
    答:不是的,可以交叉发生,即在并发周期执行过程中,是可以发生Minor gc的,这个找个gc日志就可以观察到。
  • CMS的并发收集周期会扫描哪些对象?会回收哪些对象?
    答:CMS的并发周期只会回收老年代的对象,但是在标记老年代的存活对象时,可能有些对象会被年轻代的对象引用,因此需要扫描整个堆的对象。

勘误:
如果满足下面两个条件,就不会开启可中断的预清理,直接进入重新标记阶段:

  • Eden的使用空间 < CMSScheduleRemarkEdenSizeThreshold
  • Eden的使用率 <CMSScheduleRemarkEdenPenetration
    http://mail.openjdk.java.net/pipermail/hotspot-gc-dev/2014-July/010405.html

Minor GC、Major GC和Full GC

Stop The World:Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起(除了垃圾收集帮助器之外)。Java中一种全局暂停现象所有Java代码停止,native代码可以执行,但不能与JVM交互
知乎:minor gc 会发生stop the world 现象吗?

Minor GC:年轻代(包括 Eden 和 Survivor 区域)回收内存。
当发生Minor GC事件的时候,有一些有趣的地方需要注意到:

  • 当 JVM 无法为一个新的对象分配空间时会触发 Minor GC,比如当 Eden 区满了。所以分配率越高,越频繁执行 Minor GC。
  • 内存池被填满的时候,其中的内容全部会被复制,指针会从0开始跟踪空闲内存。Eden 和 Survivor 区进行了标记和复制操作,取代了经典的标记、扫描、压缩、清理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是停留在所使用内存池的顶部。
  • 执行 Minor GC 操作时,不会影响到永久代。从永久代到年轻代的引用被当成GC roots从年轻代到永久代的引用在标记阶段被直接忽略掉(就是说年轻代对象被回收时,不会让其引用的永久代对象被回收)
  • 所有的 Minor GC都会触发“全世界的暂停(stop-the-world)”,停止应用程序的线程。对于大部分应用程序,停顿导致的延迟都是可以忽略不计的。其中的真相就 是,大部分 Eden 区中的对象都能被认为是垃圾,永远也不会被复制到 Survivor 区或者老年代空间。如果正好相反,Eden 区大部分新生对象不符合 GC 条件,Minor GC 执行时暂停的时间将会长很多。

Major GC 是清理老年代。
Full GC 是清理整个堆空间—包括年轻代和老年代。

我们不用去关心到底是叫 Major GC 还是 Full GC,大家应该关注当前的 GC 是否停止了所有应用程序的线程,还是能够并发的处理(指的是GC线程和用户线程交替进行)而不用停掉应用程序的线程。

文中显示了CMSjstat -gc所看到的2次Full GC可能只是1次Major GC(通过+PrintGCDetails查看GC日志验证): jstat 中的2次Full GC实际是两个暂停所有事件的情况,jstat 将回收初始标记和最终 Remark 阶段作为2次Full GC最初的标记阶段0.0041705s + 最后 Remark 阶段0.0462010s = 导致所有线程停止了共计50ms)。

Minor GC、Major GC和Full GC之间的区别

Minor GC ,Full GC 触发条件

Minor GC触发条件:当Eden区满时,触发Minor GC。

Full GC触发条件:
(1)调用System.gc时,系统建议执行Full GC,但是不必然执行
(2)老年代空间不足
(3)方法区空间不足
(4)通过Minor GC后进入老年代的对象平均大小 > 老年代的可用内存(Minor GC引发 Full GC
(5)由Eden区、From 区To 区复制时,对象大小 > To 区可用内存,则把该对象转存到老年代,且老年代的可用内存 < 该对象大小
https://blog.csdn.net/YHYR_YCY/article/details/52566105

你可能感兴趣的:(JVM参数、GC)