登陆

章鱼世界官网-一文搞清楚Minor GC、Major GC 、Full GC 的差异和联络

admin 2019-12-13 270人围观 ,发现0个评论

前语

文章要求读者了解 JVM 内置的通用废物收回准则。堆内存划分为 Eden、Survivor 和 Tenured/Old 空间,代假定和其他不同的 GC 算法超出了本文评论的规模。

Minor GC

从年青代空间(包含 Eden 和 Survivor 区域)收回内存被称为 Minor GC。这一界说既明晰又易于了解。可是,当发作Minor GC事情的时分,有一些风趣的当地需求注意到:

1、当 JVM 无法为一个新的目标分配空间时会触发 Minor GC,比方当 Eden 区满了。所以分配率越高,越频频履行 Minor GC。

2、内存池被填满的时分,其间的内容悉数会被仿制,指针会从0开端盯梢闲暇内章鱼世界官网-一文搞清楚Minor GC、Major GC 、Full GC 的差异和联络存。Eden 和 Survivor 区进行了符号和仿制操作,替代了经典的符号、扫描、紧缩、整理操作。所以 Eden 和 Survivor 区不存在内存碎片。写指针总是逗留在所运用内存池的顶部。

3、履行 Minor GC 操作时,不会影响到永久代。从永久代到年青代的引证被当成 GC roots,从年青代到永久代的引证在符号阶段被直接疏忽掉。

4、质疑惯例的认知,一切的 Minor GC 都会触发“全世界的暂停(stop-the-world)”,中止使用程序的线程。关于大部分使用程序,中止导致的推迟都是能够疏忽不计的。其间的本相就 是,大部分 Eden 区中的目标都能被认为是废物,永久也不会被仿制到 Survivor 区或许老时代空间。假如正好相反,Eden 区大部分重生目标不符合 GC 条件,Minor GC 履行时暂停的时刻将会长许多。

所以 Minor GC 的状况就适当清楚了——每次 Minor GC 会整理年青代的内存。

Major GC vs Full GC

咱们应该注意到,现在,这些术语无论是在 JVM 规范仍是在废物搜集研讨论文中都没有正式的界说。可是咱们一看就知道这些在咱们现已知道的根底之上做出的界说是正确的,Minor GC 整理年青带内存应该被规划得简略:

  • Major GC 是整理老时代。
  • Full GC 是整理整个堆空间—包含年青代和老时代。

很不幸,实际上它还有点杂乱且令人困惑。首要,许多 Major GC 是由 Minor GC 触发的,所以许多状况下将这两种 GC 别离是不太可能的。另一方面,许多现代废物搜集机制会整理部分永久代空间,所以运用“cleaning”一词仅仅部分正确。

这使得咱们不必去关怀到底是叫 Major GC 仍是 Full GC,咱们应该重视当时的 GC 是否中止了一切使用程序的线程,仍是能够并发的处理而不必停掉使用程序的线程。

这种紊乱乃至内置到 JVM 规范东西。下面一个比如很好的解说了我的意思。让咱们比较两个不同的东西 Concurrent Mark 和 Sweep collector (-XX:+UseConcMarkSweepGC)在 JVM 中运转时输出的盯梢记载。

第一次测验经过 jstat 输出:

my-precious: me$ jstat -gc -t 4235 1sTime S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 5.7 34048.0 34048.0 0.0 34048.0 272640.0 194699.7 1756416.0 181419.9 18304.0 17865.1 2688.0 2497.6 3 0.275 0 0.000 0.275 6.7 34048.0 34048.0 34048.0 0.0 272640.0 247555.4 1756416.0 263447.9 18816.0 18123.3 章鱼世界官网-一文搞清楚Minor GC、Major GC 、Full GC 的差异和联络2688.0 2523.1 4 0.359 0 0.000 0.359 7.7 34048.0 34048.0 0.0 34048.0 272640.0 257729.3 1756416.0 345109.8 19072.章鱼世界官网-一文搞清楚Minor GC、Major GC 、Full GC 的差异和联络0 18396.6 2688.0 2550.3 5 0.451 0 0.000 0.451 8.7 34048.0 34048.0 34048.0 34048.0 272640.0 272640.0 1756416.0 444982.5 19456.0 18681.3 2816.0 2575.8 7 0.550 0 0.000 0.550 9.7 34048.0 34048.0 34046.7 0.0 272640.0 16777.0 1756416.0 587906.3 20096.0 19235.1 2944.0 2631.8 8 0.720 0 0.000 0.72010.7 34048.0 34048.0 0.0 34046.2 272640.0 80171.6 1756416.0 664913.4 20352.0 19495.9 2944.0 2657.4 9 0.810 0 0.000 0.81011.7 34048.0 34048.0 34048.0 0.0 272640.0 129480.8 1756416.0 745100.2 20608.0 19704.5 2944.0 2678.4 10 0.896 0 0.000 0.89612.7 34048.0 34048.0 0.0 34046.6 272640.0 164070.7 1756416.0 822073.7 20992.0 19937.1 3072.0 2702.8 11 0.978 0 0.000 0.97813.7 34048.0 34048.0 34048.0 0.0 272640.0 211949.9 1756416.0 897364.4 21248.0 20179.6 3072.0 2728.1 12 1.087 1 0.004 1.09114.7 34048.0 34048.0 0.0 34047.1 272640.0 245801.5 1756416.0 597362.6 21504.0 20390.6 3072.0 2750.3 13 1.183 2 0.050 1.23315.7 34048.0 34048.0 0.0 34048.0 272640.0 21474.1 1756416.0 757347.0 22012.0 20792.0 3200.0 2791.0 15 1.336 2 0.050 1.38616.7 34048.0 34048.0 34047.0 0.0 272640.0 48378.0 1756416.0 838594.4 22268.0 21003.5 3200.0 2813.2 16 1.433 2 0.050 1.484

这个片段是 JVM 发动后第17秒提取的。根据该信息,咱们能够得出这样的成果,运转了12次 Minor GC、2次 Full GC,时刻总跨度为50毫秒。经过 jconsole 或许 jvisualvm 这样的根据GUI的东西你能得到相同的成果。

java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC eu.plumbr.demo.GarbageProducer3.157: [GC (Allocation Failure) 3.157: [ParNew: 272640K->34048K(306688K), 0.0844702 secs] 272640K->69574K(2063104K), 0.0845560 secs] [Times: user=0.23 sys=0.03, real=0.09 secs] 4.092: [GC (Allocation Failure) 4.092: [ParNew: 306688K->34048K(306688K), 0.1013723 secs] 342214K->136584K(2063104K), 0.1014307 secs] [Times: user=0.25 sys=0.05, real=0.10 secs] ... cut for brevity ...11.292: [GC (Allocation Failure) 11.292: [ParNew: 306686K->34048K(306688K), 0.0857219 secs] 971599K->779148K(2063104K), 0.0857875 secs] [Times: user=0.26 sys=0.04, real=0.09 secs] 12.140: [GC (Allocation Failure) 12.140: [ParNew: 306688K->34046K(306688K), 0.0821774 secs] 1051788K->856120K(2063104K), 0.0822400 secs] [Times: user=0.25 sys=0.03, real=0.08 secs] 12.989: [GC (Allocation Failure) 12.989: [ParNew: 306686K->34048K(306688K), 0.1086667 secs] 1128760K->931412K(2063104K), 0.1087416 secs] [Times: user=0.24 sys=0.04, real=0.11 secs] 13.098: [GC (CMS Initial Mark) [1 CMS-initial-mark: 897364K(1756416K)] 936667K(2063104K), 0.0041705 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 13.102: [CMS-concurrent-mark-start]13.章鱼世界官网-一文搞清楚Minor GC、Major GC 、Full GC 的差异和联络341: [CMS-concurrent-mark: 0.238/0.238 secs] [Times: user=0.36 sys=0.01, real=0.24 secs] 13.341: [CMS-concurrent-preclean-start]13.350: [CMS-concurrent-preclean: 0.009/0.009 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 13.350: [CMS-concurrent-abortable-preclean-start]13.878: [GC (Allocation Failure) 13.878: [ParNew: 306688K->34047K(306688K), 0.0960456 secs] 1204052K->1010638K(2063104K), 0.0961542 secs] [Times: user=0.29 sys=0.04, real=0.09 secs] 14.366: [CMS-concurrent-abortable-preclean: 0.917/1.016 secs] [Times: user=2.22 sys=0.07, real=1.01 secs] 14.366: [GC (CMS Final Remark) [YG occupancy: 182593 K (306688 K)]14.366: [Rescan (parallel) , 0.0291598 secs]14.395: [weak refs processing, 0.0000232 secs]14.395: [class unloading, 0.0117661 secs]14.407: [scrub symbol table, 0.0015323 secs]14.409: [scrub string table, 0.0003221 secs][1 CMS-remark: 976591K(1756416K)] 1159184K(2063104K), 0.0462010 secs] [Times: user=0.14 sys=0.00, real=0.05 secs] 14.412: [CMS-concurrent-sweep-start]14.633: [CMS-concurrent-sweep: 0.221/0.221 secs] [Times: user=0.37 sys=0.00, real=0.22 secs] 14.633: [CMS-concurrent-reset-start]14.636: [CMS-concurrent-reset: 0.002/0.002 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

在允许赞同这个定论之前,让咱们看看来自同一个 JVM 发动搜集的废物搜集日志的输出。明显- XX :+ PrintGCDetails 告知咱们一个不同且更具体的故事:

根据这些信息,咱们能够看到12次 Minor GC 后开端有些和上面不一样了。没有运转两次 Full GC,这不同的当地在于单个 GC 在永久代中不同阶段运转了两次:

1、开端的符号阶段,用了0.0041705秒也便是4ms左右。这个阶段会暂停“全世界( stop-the-world)”的事情,中止一切使用程序的线程,然后开端符号。

2、并行履行符号和清洗阶段。这些都是和使用程序线程并行的。

3、终究 Remark 阶段,花费了0.0462010秒约46ms。这个阶段会再次暂停一切的事情。

4、并行履行整理操作。正如其名,此阶段也是并行的,不疯狂的兔子会中止其他线程。

所以,正如咱们从废物收回日志中所看到的那样,实际上仅仅履行了 Major GC 去整理老时代空间罢了,而不是履行了两次 Full GC。

假如你是后期做决 定的话,那么由 jstat 供给的数据会引导你做出正确的决议计划。它正确列出的两个暂停一切事情的状况,导致一切线程中止了合计50ms。可是假如你企图优化吞吐量,你会被误导的。清 单只列出了收回初始符号和终究 Remark 阶段,jstat的输出看不到那些并发完结的作业。

定论

考虑到这种状况,最好防止以 Minor、Major、Full GC 这种方法来思考问题。而应该监控使用推迟或许吞吐量,然后将 GC 事情和成果联系起来。

跟着这些 GC 事情的发作,你需求额定的重视某些信息,GC 事情是强制一切使用程序线程中止了仍是并行的处理了部分事情。

终究

欢迎咱们一同沟通,喜爱文章记住重视我点赞转发哟,感谢支撑!

请关注微信公众号
微信二维码
不容错过
Powered By Z-BlogPHP