专栏首页JVMGCJava-Parallel GC介绍
原创

Java-Parallel GC介绍

JVM 垃圾收集器发展历史

image-20201205221520220

JDK1.8中使用jmap -heap pid上面会出现Parallel GC

jmap -heap 18378
Attaching to process ID 18378, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.261-b12

using thread-local object allocation.
Parallel GC with 4 thread(s) ###

JVM垃圾收集器的发展历史中,我们并没有找到Parallel GC,那么它到底代表什么?

Parallel GC有两种组合

  • 使用-XX:+UseParallelGC参数来启用Parallel ScavengePSMarkSweep(Serial Old)收集器组合进行垃圾收集。(图上可以找到)
  • 使用-XX:+UserParallelOldGC参数来启用Parallel scavengeParallel Old收集器组合收集。(图上可以找到)

Parallel GC起源

Young GC / Parallel Scavenge

Parallel Scavenge收集器(下称PS收集器)也是一个多线程收集器,也是使用复制算法,但它的对象分配规则与回收策略都与ParNew收集器有所不同,它是以吞吐量最大化(即GC时间占总运行时间最小)为目标的收集器实现,它允许较长时间的STW换取总吞吐量最大化。

Full GC / PSMarkSweep(Serial Old)

在Parallel Scavenge收集器架构中本身有PS MarkSweep收集器来进行老年代收集,但由于PS MarkSweep与Serial Old实现非常接近,因此官方的许多资料都直接以Serial Old代替PS MarkSweep进行讲解。

使用-XX:+UseParallelGC参数便是开启PSScavengePSMarkSweep的组合,即是只有Young GC是并行的,Full GC仍然是串行,使用标记-整理算法。

Full GC / PSCompact(ParallelOld GC)

后来开发者开发了基于LISP2算法的并行版的Full GC收集器来收集整个GC堆,名为PSCompact。使用-XX:+UseParallelOldGC参数来便是开启PSScavengePSCompact的组合,将Young GCFull GC都并行化了。

收集器

Parallel Scavenge

新生代并行回收器,内存分布使用的复制算法。Parallel Scavenge主要关注的是应用的吞吐量,而其他收集器关注的主要是尽可能的缩短STW(stop the word)的时间。

吞度量=t1/(t1+t2)
t1运行用户代码的总时间
t2运行垃圾收集的总时间
比如,虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

Parallel Scavenge收集器提供了两个参数来用于精确控制吞吐量,一是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数,二是控制吞吐量大小的-XX:GCTimeRatio参数

  • -XX:MaxGCPauseMillis

参数的值是一个大于0的毫秒数,收集器将尽可能的保证回收耗费的时间不超过设定的值,但是,并不是越小越好,GC停顿时间缩短是以牺牲吞吐量和新生代空间来换取的,如果设置的值太小,将会导致频繁GC,这样虽然GC停顿时间下来了,但是吞吐量也下来了。比如收集500MB时候,需要每10秒收集一次,每次回收耗时100ms;如果收集300MB的时候,需要每5秒收集一次,每次回收耗时70ms,虽然每次回收耗时更少,但是工作频次提高,导致吞吐量反而降低了。

  • -XX:GCTimeRatio

参数的值是一个大于0且小于100的整数,也就是垃圾收集时间占总时间的比率,默认值是99,就是允许最大1%(即1/(1+99))的垃圾收集时间。

Parallel Scavenge有个重要的特性,是支持GC自适应的调节策略,使用-XX:UseAdaptiveSizePolicy参数开启,开启之后,虚拟机会根据当前系统运行情况收集监控信息,动态调整新生代的比例、老年大大小等细节参数,以提供最合适的停顿时间或最大的吞吐量。开启这个参数之后,就不需要再设置新生代大小,Eden与S0/S1的比例等等参数。

Parallel Old

img

Parallel Old GCParallel ScavengeParallel Old收集器组合中,负责Full GC,是一个并行收集器,其在整理年轻代的时候,使用与Parallel Scavenge GC一样的常规“复制”算法,但是在整理老年代的时候,是使用的基于“标记-整理”算法优化的“Mark–Summary-Compaction”算法。

算法包含三个部分

  • Mark

首先将老年代的内存,划分为大小固定的多个连续Region,当标记完存活对象之后,统计每个Region的存活对象数量。Mark阶段采用串行标记所有从GC Roots可直达的对象,然后并行标记所有存活的对象。

  • Summary

某个Region的密度 = 存活对象的内存大小 / Region内存大小。因为每次整理会将存活的对象向Old区的左侧移动,而对象存活越久,理论上就越不容易被回收,所以经过多次整理之后,左侧Region中的对象更偏向于稳定、“长寿”,即是左侧Region的密度更大。Summary阶段,算法采用以空间换时间的优化方式,针对一个密度很大的Region,比如95%的空间是存活对象,只有断断续续5%的空间是未使用的,那么算法认为这个Region不值得被整理,即是选择浪费掉这5%的空间,以节省整理操作的时间开销。在Sumamry阶段,首先从左至右计算各个Region的密度,直到找到一个point,这个point左侧的Region都不值得整理,右侧的Region需要整理。point左侧的Region被称为dense prefix,这个区域内的对象都不会被移动。Summary阶段是一个串行执行的阶段。

  • Compaction

Compaction阶段利用Summary阶段的统计数据,针对需要整理的部分,采用“整理”算法进行并行操作。

GC策略

  • -XX:+ScavengeBeforeFullGC

ScavengeBeforeFullGCParallel GC套装中(两种组合都生效)的一个参数,默认是开启的,作用是在一次Full GC之前,先触发一次Young GC来清理年轻代,以降低Full GC的STW耗时(Young GC会清理Young GC中非存活的对象,减少Full GC中,标记存活对象的工作量)。

举个例子,使用System.gc()触发Full GC,可以看到日志如下:

2020-03-01T13:38:30.496-0800: [GC (System.gc()) [PSYoungGen: 37274K->1392K(46080K)] 78234K->42360K(97280K), 0.0033397 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 
2020-03-01T13:38:30.500-0800: [Full GC (System.gc()) [PSYoungGen: 1392K->0K(46080K)] [ParOldGen: 40968K->1225K(51200K)] 42360K->1225K(97280K), [Metaspace: 4876K->4876K(1056768K)], 0.0113851 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]

第一次GC为一次Young GC,可以看到是由System.gc()触发的,然后紧跟着是一次Full GC。

添加-XX:-ScavengeBeforeFullGC参数之后,日志就变为只有一条Full GC的日志:

2020-03-01T14:26:05.562-0800: [Full GC (System.gc()) [PSYoungGen: 37274K->0K(46080K)] [ParOldGen: 40960K->1225K(51200K)] 78234K->1225K(97280K), [Metaspace: 4882K->4882K(1056768K)], 0.0127785 secs] [Times: user=0.05 sys=0.01, real=0.01 secs]

内存分配策略

对于常规收集器来说,当Eden区无法分配内存时,便会触发一次Young GC,但是对于Parallel GC有点变化:

  • 当整个新生代剩余的空间无法存放某个对象时,Parallel GC中该对象会直接进入老年代;
  • 而如果整个新生代剩余的空间可以存放但只是Eden区空间不足,则会尝试一次Minor GC。

举个例子:

public class TestApp {
    public static void main(String[] args) throws InterruptedException {
        allocM(10);
        allocM(10);
        allocM(10);
        allocM(20);

        Thread.sleep(1000);
    }

    private static byte[] allocM(int n) throws InterruptedException {
        byte[] ret = new byte[1024 * 1024 * n];

        System.out.println(String.format("%s: Alloc %dMB", LocalDateTime.now().toString(), n));
        Thread.sleep(500);

        return ret;
    }
}

JVM参数为:-Xms100m -Xmx100m -Xmn50m -XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+UseParallelOldGC,运行起来,打印日志如下:

2020-03-01T16:36:13.027: Alloc 10MB
2020-03-01T16:36:13.548: Alloc 10MB
2020-03-01T16:36:14.061: Alloc 10MB
2020-03-01T16:36:14.577: Alloc 20MB
Heap
 PSYoungGen      total 46080K, used 38094K [0x00000007bce00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 40960K, 93% used [0x00000007bce00000,0x00000007bf333878,0x00000007bf600000)
  from space 5120K, 0% used [0x00000007bfb00000,0x00000007bfb00000,0x00000007c0000000)
  to   space 5120K, 0% used [0x00000007bf600000,0x00000007bf600000,0x00000007bfb00000)
 ParOldGen       total 51200K, used 20480K [0x00000007b9c00000, 0x00000007bce00000, 0x00000007bce00000)
  object space 51200K, 40% used [0x00000007b9c00000,0x00000007bb000010,0x00000007bce00000)
 Metaspace       used 4879K, capacity 5012K, committed 5248K, reserved 1056768K
  class space    used 527K, capacity 564K, committed 640K, reserved 1048576K

可以看到第4行,分配20M内存时,Eden区已经不足20M空余内存了,整个年轻代加起来都不够20M了,但是并没有触发Young GC,而是继续执行,知道程序结束前,打印堆的情况,我们可以看到20M内存是分配到了老年代中。

修改代码,将最后一个allocM(20);改成allocM(5);,重新执行,得到日志如下:

2020-03-01T16:39:56.375: Alloc 10MB
2020-03-01T16:39:56.896: Alloc 10MB
2020-03-01T16:39:57.408: Alloc 10MB

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 37274K [0x00000007bce00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 40960K, 91% used [0x00000007bce00000,0x00000007bf266a08,0x00000007bf600000)
  from space 5120K, 0% used [0x00000007bfb00000,0x00000007bfb00000,0x00000007c0000000)
  to   space 5120K, 0% used [0x00000007bf600000,0x00000007bf600000,0x00000007bfb00000)
 ParOldGen       total 51200K, used 0K [0x00000007b9c00000, 0x00000007bce00000, 0x00000007bce00000)
  object space 51200K, 0% used [0x00000007b9c00000,0x00000007b9c00000,0x00000007bce00000)
 Metaspace       used 4882K, capacity 5012K, committed 5248K, reserved 1056768K
  class space    used 526K, capacity 564K, committed 640K, reserved 1048576K
2020-03-01T16:39:57.910-0800: [GC (Allocation Failure) [PSYoungGen: 37274K->1328K(46080K)] 37274K->1336K(97280K), 0.0033380 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 1328K [0x00000007bce00000, 0x00000007c0000000, 0x00000007c0000000)
  eden space 40960K, 0% used [0x00000007bce00000,0x00000007bce00000,0x00000007bf600000)
  from space 5120K, 25% used [0x00000007bf600000,0x00000007bf74c010,0x00000007bfb00000)
  to   space 5120K, 0% used [0x00000007bfb00000,0x00000007bfb00000,0x00000007c0000000)
 ParOldGen       total 51200K, used 8K [0x00000007b9c00000, 0x00000007bce00000, 0x00000007bce00000)
  object space 51200K, 0% used [0x00000007b9c00000,0x00000007b9c02000,0x00000007bce00000)
 Metaspace       used 4882K, capacity 5012K, committed 5248K, reserved 1056768K
  class space    used 526K, capacity 564K, committed 640K, reserved 1048576K
}

2020-03-01T16:39:57.916: Alloc 5MB

在执行第4行,分配5M内存时,Eden区不足,但是整个年轻代空余内存是大于5M的,于是触发了一次Young GC。

悲观策略

绝大多数收集器,都有这么一个策略:在执行Young GC之前,如果估计之前晋升老年代的平均大小,比当前老年代的剩余空间要大的话,则会放弃Young GC,转而触发Full GC。

Parallel GC除了上述策略外,还有另外一个策略:在执行Young GC之后,如果晋升老年代的平均大小,比当前老年代的剩余空间要大的话,则会触发一次Full GC。

public class TestApp {
    public static void main(String[] args) throws InterruptedException {
        byte[][] use = new byte[7][];
        use[0] = allocM(10);
        use[1] = allocM(10);
        use[2] = allocM(10);
        use[3] = allocM(10);
        use[4] = allocM(10);
        use[5] = allocM(10);
        use[6] = allocM(10);

        Thread.sleep(1000);
    }

    private static byte[] allocM(int n) throws InterruptedException {
        byte[] ret = new byte[1024 * 1024 * n];

        System.out.println(String.format("%s: Alloc %dMB", LocalDateTime.now().toString(), n));
        Thread.sleep(500);

        return ret;
    }
}

JVM参数为:-Xms100m -Xmx100m -Xmn50m -XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -XX:+UseParallelOldGC,运行起来,打印日志如下(省略掉部分):

2020-03-01T16:02:43.172: Alloc 10MB
2020-03-01T16:02:43.693: Alloc 10MB
2020-03-01T16:02:44.206: Alloc 10MB

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 37274K [*, *, *)
  eden space 40960K, 91% used [*,*,*)
  from space 5120K, 0% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 51200K, used 0K [*, *, *)
  object space 51200K, 0% used [*,*,*)
2020-03-01T16:02:44.711-0800: [GC (Allocation Failure) [PSYoungGen: 37274K->1392K(46080K)] 37274K->32120K(97280K), 0.0163176 secs] [Times: user=0.09 sys=0.03, real=0.01 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 1392K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 27% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 51200K, used 30728K [*, *, *)
  object space 51200K, 60% used [*,*,*)
}

{Heap before GC invocations=2 (full 1):
 PSYoungGen      total 46080K, used 1392K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 27% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 51200K, used 30728K [*, *, *)
  object space 51200K, 60% used [*,*,*)
2020-03-01T16:02:44.728-0800: [Full GC (Ergonomics) [PSYoungGen: 1392K->0K(46080K)] [ParOldGen: 30728K->31945K(51200K)] 32120K->31945K(97280K), [Metaspace: 4881K->4881K(1056768K)], 0.0096352 secs] [Times: user=0.05 sys=0.00, real=0.01 secs] 
Heap after GC invocations=2 (full 1):
 PSYoungGen      total 46080K, used 0K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 0% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 51200K, used 31945K [*, *, *)
  object space 51200K, 62% used [*,*,*)
}

2020-03-01T16:02:44.739: Alloc 10MB

执行完第一次Young GC之后,由于年轻代的S区容量不足,所以Eden区中的30M内存会提前晋升到老年代。GC之后,老年代空间被占用了60%,还剩下40%(20M),而平均晋升内存大小为30M,所以触发悲观策略,导致了一次Full GC

我们将JVM中的-Xms100m -Xmx100m换成-Xms120m -Xmx120m,重新执行日志如下:

2020-03-01T16:08:39.372: Alloc 10MB
2020-03-01T16:08:39.895: Alloc 10MB
2020-03-01T16:08:40.405: Alloc 10MB

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 37274K [*, *, *)
  eden space 40960K, 91% used [*,*,*)
  from space 5120K, 0% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 0K [*, *, *)
  object space 71680K, 0% used [*,*,*)
2020-03-01T16:08:40.906-0800: [GC (Allocation Failure) [PSYoungGen: 37274K->1360K(46080K)] 37274K->32088K(117760K), 0.0152322 secs] [Times: user=0.07 sys=0.03, real=0.02 secs] 
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 46080K, used 1360K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 26% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 30728K [*, *, *)
  object space 71680K, 42% used [*,*,*)
}

2020-03-01T16:08:40.923: Alloc 10MB
2020-03-01T16:08:41.429: Alloc 10MB
2020-03-01T16:08:41.934: Alloc 10MB

{Heap before GC invocations=2 (full 0):
 PSYoungGen      total 46080K, used 32854K [*, *, *)
  eden space 40960K, 76% used [*,*,*)
  from space 5120K, 26% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 30728K [*, *, *)
  object space 71680K, 42% used [*,*,*)
2020-03-01T16:08:42.438-0800: [GC (Allocation Failure) [PSYoungGen: 32854K->1392K(46080K)] 63582K->62840K(117760K), 0.0151558 secs] [Times: user=0.07 sys=0.03, real=0.02 secs] 
Heap after GC invocations=2 (full 0):
 PSYoungGen      total 46080K, used 1392K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 27% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 61448K [*, *, *)
  object space 71680K, 85% used [*,*,*)
}

{Heap before GC invocations=3 (full 1):
 PSYoungGen      total 46080K, used 1392K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 27% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 61448K [*, *, *)
  object space 71680K, 85% used [*,*,*)
 Metaspace       used 4883K, capacity 5012K, committed 5248K, reserved 1056768K
  class space    used 526K, capacity 564K, committed 640K, reserved 1048576K
2020-03-01T16:08:42.454-0800: [Full GC (Ergonomics) [PSYoungGen: 1392K->0K(46080K)] [ParOldGen: 61448K->62634K(71680K)] 62840K->62634K(117760K), [Metaspace: 4883K->4883K(1056768K)], 0.0139615 secs] [Times: user=0.08 sys=0.00, real=0.01 secs] 
Heap after GC invocations=3 (full 1):
 PSYoungGen      total 46080K, used 0K [*, *, *)
  eden space 40960K, 0% used [*,*,*)
  from space 5120K, 0% used [*,*,*)
  to   space 5120K, 0% used [*,*,*)
 ParOldGen       total 71680K, used 62634K [*, *, *)
  object space 71680K, 87% used [*,*,*)
}

2020-03-01T16:08:42.469: Alloc 10MB

可以看到,第一次Young GC之后,由于老年代剩余空间足够大,并没有触发Full GC,而随着内存继续分配,第二次Young GC之后,还是触发了悲观策略。

JVM默认老年代回收是 PSMarkSweep(Serial-Old) 还是Parallel Old?

这个改进使得HotSpot VM在选择使用ParallelGC(-XX:+UseParallelGC 或者是ergonomics自动选择)的时候,会默认开启 -XX:+UseParallelOldGC 。这个变更应该是在JDK7u4开始的JDK7u系列与JDK8系列开始生效。

http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/rev/24cae3e4cbaa

--- a/src/share/vm/runtime/arguments.cpp	Mon Jan 30 15:21:57 2012 +0100
+++ b/src/share/vm/runtime/arguments.cpp	Thu Feb 02 16:05:17 2012 -0800
@@ -1400,10 +1400,11 @@
 
 void Arguments::set_parallel_gc_flags() {
   assert(UseParallelGC || UseParallelOldGC, "Error");
-  // If parallel old was requested, automatically enable parallel scavenge.
-  if (UseParallelOldGC && !UseParallelGC && FLAG_IS_DEFAULT(UseParallelGC)) {
-    FLAG_SET_DEFAULT(UseParallelGC, true);
+  // Enable ParallelOld unless it was explicitly disabled (cmd line or rc file).
+  if (FLAG_IS_DEFAULT(UseParallelOldGC)) {
+    FLAG_SET_DEFAULT(UseParallelOldGC, true);
   }
+  FLAG_SET_DEFAULT(UseParallelGC, true);
 
   // If no heap maximum was requested explicitly, use some reasonable fraction
   // of the physical memory, up to a maximum of 1GB.

在这个改变之前,即便选择了ParallelGC,默认情况下ParallelOldGC并不会随即开启,而是要自己通过 -XX:+UseParallelOldGC 去选定。

在GC日志里,如果看到Full GC里有"ParOldGen"就是选择了ParallelOldGC。 Full GC [PSYoungGen: 480K->0K(3584K)] [ParOldGen: 4660K->4909K(12288K)] 5141K->4909K(15872K) [PSPermGen: 11202K->11198K(22528K)], 0.0515530 secs

wx.jpg

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • JVM06-经典垃圾收集器

    前言相关概念并行和并发吞吐量(Throughput)Minor GC和Full GC新生代收集器Serial收集器ParNew收集器Parallel Scave...

    码农飞哥
  • 成为JavaGC专家(1)—深入浅出Java垃圾回收机制

    对于Java开发人员来说,了解垃圾回收机制(GC)有哪些好处呢?首先可以满足作为一名软件工程师的求知欲,其次,深入了解GC如何工作可以帮你写出更好的Java应用...

    哲洛不闹
  • Hotspot JVM垃圾回收器

      前两篇《JVM入门——运行时数据区》《JVM常见垃圾回收算法》所提到的实际上JVM规范以及常用的垃圾回收算法,具体的JVM实现实际上不止一种,有JRocki...

    用户1148394
  • JVM垃圾回收(下)

    接着上一篇,介绍完了 JVM 中识别需要回收的垃圾对象之后,这一篇我们来说说 JVM 是如何进行垃圾回收。

    健程之道
  • 我眼中的G1 GC

    7岁那年,当我合上《上下五千年》一套三册全书时,我对自己说,我想当个作家。这一晃27年了,等待了27年,我的第一本书《大话Java性能优化》在2016年4月正式...

    博文视点Broadview
  • Java虚拟机详解(六)------内存分配

      我们说Java是自动进行内存管理的,所谓自动化就是,不需要程序员操心,Java会自动进行内存分配和内存回收这两方面。

    IT可乐
  • Java内存区域和GC机制

      Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编写...

    Java团长
  • 软件架构-JVM(中)的垃圾回收机制详解

    1.对新生代的对象的收集称为minor GC;2.对旧生代的对象的收集称为Full GC;3.程序中主动调用System.gc()强制执行的GC为Full GC...

    IT架构圈
  • JVM层GC调优(下)

    本文是 JVM层GC调优(上) 的后续,在上一篇文章中,我们介绍了JVM的内存结构、常见的垃圾回收算法以及垃圾收集器和不同收集器中的一些GC调优参数。所以通过上...

    端碗吹水
  • 7 种 JVM 垃圾收集器,看完我跪了。。

    Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别,并且一般都会提供参数供用户根据自己...

    Bug开发工程师
  • Java 内存区域和GC机制

    Java垃圾回收概况   Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发...

    汤高
  • 深入理解JVM,7种垃圾收集器,看完我跪了!

    如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定,因此不同的厂商、版本的虚拟机...

    麦洛
  • 【转】Java之 内存区域和GC机制

       Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之一,作为Java开发者,一般不需要专门编...

    shirayner
  • 聊聊JVM中的垃圾回收(GC)

    Java相对于C/C++语言来说,最明显的特点在于Java引入了自动垃圾回收。垃圾回收(Garbage Collection简称GC)可以使程序员不在需要关心J...

    java技术爱好者
  • 面试官:简历上说精通垃圾收集器?来吧,挨个给我说一遍

    上文已经讲解垃圾收集的各种算法,算法可以理解为方法,如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

    胖虎
  • 深入理解JVM虚拟机3:垃圾回收器详解

    本文转自:https://www.cnblogs.com/snailclimb/p/9086341.html

    Java技术江湖
  • 小师妹学JVM之:GC的垃圾回收算法

    JVM的重要性不言而喻了,如果把java的应用程序比作一辆跑车,那么JVM就是这辆车的发动机,没有它,java程序就成了空中楼阁,无根浮萍。而在JVM中有一块内...

    程序那些事
  • 看完这篇,我再也不怕面试官问垃圾收集了

    说在前面:本文的篇幅较长,看本文的时候最好先去上个厕所,先准备好一杯枸杞茶,慢慢品,本文将会讲解三种垃圾收集算法:标记-清除、复制、标记-整理算法,以及各种成熟...

    用户5546570
  • JVM 学习笔记(五)

      前面的文件介绍了JVM的内存模型以及各个区域存放了那些内容,本编文章将介绍JVM中的垃圾回收Garbage Collector,和大家一起探讨一下。

    会说话的丶猫

扫码关注云+社区

领取腾讯云代金券