Icharle | Don't forget your first thoughts - JVM
新生代GC(Minor GC)：指发生在新生代的垃圾收集动作，因为Java对象大多都具备朝生夕灭的特性，所以Minor GC非常频繁，一般回收速度也比较快。 老年代(Major GC/Full GC)：指发生在老年代的GC，出现了Major GC，经常会伴随至少一次的Minor GC(但非绝对的，在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minior GC慢10倍以上。 内存分配策略 JVM虚拟机提供了-XX：+PrintGCDetails这个收集器日志参数，告诉虚拟机在发生垃圾收集行为时打印内存回收日志，并且在进程退出的时候输出当前的内存各区域分配情况。 对象优先在Eden分配 在大多数情况，对象在新生代Eden区中分配，当Eden区分配没有足够空间进行分配时，虚拟机将发起一次Minor GC。 大对象直接进入老年代 大对象是指，需要大量连续内存空间的Java对象，典型的大对象就是那种很长很长的字符串以及数组。 经常出现大对象容易导致内存还有不少空间就提前触发垃圾收集以获取足够的连续空间来存放大对象。 JVM虚拟机提供了一个-XX：PretenureSizeThreshold参数，令大于这个设置值的对象直接在老年代分配。注意：PretenureSizeThreshold参数只对Serial和ParNew两个收集器有效。 长期存活的对象将进入老年代 JVM虚拟机采用分代收集的思想来管理内存，则内存回收也需要识别何为新生代，何为老年代，因此JVM定义了一对象年龄(Age)计数器。对象在Survior区中每“熬过”一次Minor GC，年龄就增加1岁，默认为15岁即可晋升老年代。 对象晋升老年代阈值，可以通过-XX：MaxTenuringThreshold设置。 动态对象年龄判定 JVM虚拟机并不是永远地要求对象的年龄必须达到MaxTenuringThreshold才能晋升老年代，如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半，年龄大于或等于该年龄的对象就可以直接进入老年代，无须等到MaxTenuringthreshold中要求的年龄。 空间分配担保 在发生Minor GC之前，虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间。 如果这个条件成立，那么Minor GC可以确保是安全的。如果不成立，则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。 如果允许，则会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小，如果大于，将尝试着进行一次Minor GC，尽管这次Minor GC是有风险的；如果小于，或者HandlePromotionFailure设置不允许冒险，则改为进行一次Full GC。
7种作用于不同分代的收集器，如果两个收集器之间存在连线，就说明可以搭配使用。 Serial收集器 JVM虚拟机运行在Client模式下的默认新生代收集器。 是一个单线程收集器，单线程即说明它只会使用一个CPU或者一条收集线程去完成垃圾收集工作，更重要的是在它进行垃圾收集时，必须暂停其他所有的工作线程，直到它收集结束。 简单而高效(与其他收集器的单线程比)，对于限定单个CPU的环境来说，Serial收集器由于没有线程交互的开销，因此获得最高的单线程收集效率 在Client场景下，分配给虚拟机管理的内存一般来说不会很大，收集几十兆甚至一两百兆的新生代停顿时间完全可以控制在几十毫秒最多一百多毫秒以内。 ParNew收集器 是Serial收集器的多线程版本 运行在Server模式下的虚拟机首选的新生代收集器 除Serial收集器外，目前只有它能与CMS收集器配合工作 由于存在线程交互的开销，ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果。随着CPU数量增加，默认开启的收集器线程数与CPU的数量相同，可以使用-XX：ParallelGCThreads参数来限制垃圾收集的线程数。 Parallel Scavenge收集器 多线程新生代收集器 基于复制算法实现 其它收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间，而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。吞吐量是指：CPU用于运行代码的时间与CPU总消耗时间的比值，即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间) Serial Old收集器 是Serial收集器的老年代版本，是一个单线程收集器 采用“标记-整理”算法 Client模式下的虚拟机使用 在Server模式下，则它主要还有两大用途：一种用途是在JDK1.5以及之前版本中与Parallel Scavenge收集器搭配使用；另外一种用途是作为CMS收集器的后备预案，在并发收集发生Concurrent Mode Failure时使用。 Parallel Old收集器 是Parallel Scavenge收集器的老年代版本，是一个多线程收集器 采用“标记-整理”算法 新生代Parallel Scavenge收集器由于无法与CMS收集器配合工作，只能与Serial Old收集器配合工作，但该收集器在服务端性能差，使用了Parallel Scavenge收集器也未必能在整体应用上获得吞吐量最大化的效果，由于单线程的老年代收集无法充分利用服务器多CPU的处理能力，在老年代很大而且硬件比较高级的环境还不一定有ParNew+CMS的组合 Parallel Old收集器的出现，在注重吞吐量以及CPU资源敏感的场合，优先考虑Parallel Scacenge + Parallel Old收集器。 CMS收集器 一种以获取最短回收停顿时间为目标的收集器 采用“标记-清除”算法 收集过程分为4步： 初始标记(CMS initial mark)：仅仅只是标记一下GC Roots能直接关联到的对象，速度很快，需要停顿 并发标记(CMS concurrent mark)：进行RootsTracing过程，不需要停顿，耗时长 重新标记(CMS remark)：为了修正并发标记期间因用户重新继续运作而导致标记产生的变动的哪一部分对象的标记记录，需要停顿 并发清除(CMS concurrent sweep)：不需要停顿 由于整个过程中耗时最长的并发标记和并发清除过程收集器线程可以与用户线程一起工作，不需要停顿。 存在的缺点： CMS收集器对CPU资源非常敏感，它虽然不会导致用户线程停顿，但会因为占用一部分线程(CPU资源)而导致应用程序变慢，总吞吐量降低。 无法处理浮动垃圾，可能出现Concurrent Mode Failure失败而导致另一次Full GC的产生。浮动垃圾是指由于CMS并发清理阶段用户线程还在运行着，伴随着程序运行自然会有新的垃圾不断产生，这一部分垃圾出现在标记过程之后，CMS无法在打次收集中处理掉他们，只好留待下一次GC时再清理掉。CMS运行期间预留的内存无法满足程序需要，就会出现一次Concurrent Mode Failure失败，则将启动后备预案：临时请用Serial Old收集器重新进行老年代的垃圾收集，造成停顿时间加长，性能下降。 基于“标记-清除”算法，会有大量的空间碎片产生，往往会出现老年代还有很大空间剩余，但无法找到足够大的连续空间来分配当前对象，不得不提前触发一次Full GC。 G1收集器 面向服务端应用的垃圾收集器 具备特点： 并行与并发：G1能充分利用多CPU、多核环境下的硬件优势，使用多个CPU(CPU或者CPU核心)来缩短停顿时间，部分收集器原需要停顿执行GC动作，G1收集器可以通过并发的方式让Java程序继续执行。 分代收集：与其它收集器一样，分代概念在G1中依然得到保留。可以不需要其它收集器配合就能独立管理整个GC堆。 空间整合：从整体来看是基于”标记-整理“算法实现的收集器，从局部(两个Region之间)上来看是基于”复制“算法实现，但无论如何这两种算法都意味着G1运作期间不会产生内存空间碎片。 可预测的停顿：降低停顿时间是G1和CMS共同的关注点，但G1除了追求低停顿外，还建立可预测的停顿时间模型，能让使用者明确指定在一个长度为M毫秒的时间判断内，消耗在垃圾收集上的时间不得超过N毫秒。 G1收集器，它将整个Java堆划分为多个大小相等的独立区域(Region)，虽然还保留有新生代和老年代的概念，但新生代和老年代不在物理隔离，它们都是一部分Region的结合。 G1收集器之所以能建立可预测的停顿时间模型，是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需要的经验值)，在后台维护一个优先列表，每次根据允许的收集时间，优先回收价值最大的Region。 每个Region都有一个与之对应的Remembered Set，用来检查Reference引用的对象是否处于不同的Region之中，从而保证不对全堆扫描也不会有遗漏。 不维护Remembered Set，将分为下面4个步骤 初始标记(Initial Marking)：仅仅只是标记一下GC Roots能直接关联到的对象，并修改TEMS(Next Top at Mark Start)的值，让下一阶段用户程序并发允许时，能够正确可用Region中创建新对象，该阶段需要停顿，但耗时很短。 并发标记(Concurrent Marking)：从GC Root开始对堆中对象进行可达性分析，找出存活的对象，该阶段比较耗时，但可与用户程序并发执行。 最终标记(Final Marking)：为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录，虚拟机将这端时间对象变化记录在线程Reembered Set Logs里面，并把Remembered Set Logs的数据合并到Remembered Set中，该阶段需要停顿线程，但是可并行执行。 筛选回收(Live Data Counting Evacuation)：首先对各个Region的回收价值和成本进行排序，根据用户所期望的GC停顿时间来制定回收计划。此阶段其实也可以做到与用户程序一起并发执行，但是因为只回收一部分 Region，时间是用户可控制的，而且停顿用户线程将大幅度提高收集效率。