Serial收集器


如果说垃圾回收算法是内存回收的方法论,那么垃圾收集器就是具体实现。jvm会结合针对不同的场景及用户的配置使用不同的收集器。

Serial垃圾回收器

现代的商用虚拟机的都是采用分代收集的,不同的区域用不同的收集器。常用的7种收集器,其适用的范围如图所示

常用的7种收集器

年轻代收集器
Serial、ParNew、Parallel Scavenge
老年代收集器
Serial Old、Parallel Old、CMS收集器
特殊收集器
G1收集器[新型,不在年轻、老年代范畴内]

Serial

收集过程

Serial 收集器是最基础、历史最悠久的收集器,曾经(JDK1.3.1之前) 是HotSpot 新生代收集器的唯一选择,对应的老年代是 Serial Old 收集器。

Serial:基于复制算法,Serial Old:基于标记-整理算法。

两大特点:

1.使用一个处理器或一条收集线程取完成垃圾收集工作;

2.进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。

缺点:

多线程环境下,效率低下。

优点:

1.简单而高效,迄今为止仍然时HotSpot 虚拟机在客户端模式下的默认新生代收集器;

2.在所有收集器里额外内存消耗最小。

查看JVM使用的默认的垃圾收集器

cmd执行命令:

java -XX:+PrintCommandLineFlags -version

输出如下(举例):

C:\Users\wang>java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=265625408 -XX:MaxHeapSize=4250006528 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)

自JDK7u4开始的JDK7u系列与JDK8系列,如果指定了:-XX:+UseParallelGC,则会默认开启:XX:+UseParallelOldGC 。

验证默认垃圾回收器

简单的controller代码

@RestController
@RequestMapping("/jvm")
@Slf4j
public class JvmController {

    static List<Object> list = new ArrayList<>();

    @RequestMapping("/addlist")
    public  void lust(@RequestParam("value") Integer value) throws InterruptedException {
        byte[] bytes = new byte[value * 1024 * 1024];
        list.add(bytes);
    }

    @RequestMapping("/clearlist")
    public  void clearlist() throws InterruptedException {
        list.clear();
    }
}

JVM参数,显式使用-XX:+UseParallelGC

-Xms100M
-Xmx100M
-Xmn70M
-XX:PretenureSizeThreshold=5M
-XX:+UseParallelGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintGCCause
-Xloggc:gc-old.log
-verbose:gc

启动SpringBoot项目

C:\Users\wang>jps
14864 RemoteJdbcServer
16324
1716 Launcher
21492 Jps
10492 Application
10668 RemoteJdbcServer
10796 RemoteMavenServer36
12668

C:\Users\wang>jmap -heap 10492
Attaching to process ID 10492, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

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

Heap Configuration:
   MinHeapFreeRatio         = 0
   MaxHeapFreeRatio         = 100
   MaxHeapSize              = 104857600 (100.0MB)
   NewSize                  = 73400320 (70.0MB)
   MaxNewSize               = 73400320 (70.0MB)
   OldSize                  = 31457280 (30.0MB)
   NewRatio                 = 2
   SurvivorRatio            = 8
   MetaspaceSize            = 21807104 (20.796875MB)
   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   MaxMetaspaceSize         = 17592186044415 MB
   G1HeapRegionSize         = 0 (0.0MB)

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 31457280 (30.0MB)
   used     = 14946488 (14.254081726074219MB)
   free     = 16510792 (15.745918273925781MB)
   47.51360575358073% used
From Space:
   capacity = 20971520 (20.0MB)
   used     = 13754640 (13.117446899414062MB)
   free     = 7216880 (6.8825531005859375MB)
   65.58723449707031% used
To Space:
   capacity = 20971520 (20.0MB)
   used     = 0 (0.0MB)
   free     = 20971520 (20.0MB)
   0.0% used
PS Old Generation
   capacity = 31457280 (30.0MB)
   used     = 20363760 (19.420394897460938MB)
   free     = 11093520 (10.579605102539062MB)
   64.73464965820312% used

20519 interned Strings occupying 1861040 bytes.

然后我们不断地调用

http://localhost/jvm/addlist?value=10

很容易就能触发old gc。

此时,查看我们的gc日志,下边红色我标出了ParOldGen字样:

2022-03-01T11:14:24.031+0800: 451.667: [Full GC (Ergonomics) [PSYoungGen: 12566K->3792K(51712K)] [ParOldGen: 30158K->30275K(30720K)] 42725K->34067K(82432K), [Metaspace: 53823K->53480K(1099776K)], 0.1418099 secs] [Times: user=0.70 sys=0.00, real=0.14 secs] 
2022-03-01T11:14:25.402+0800: 453.037: [Full GC (Ergonomics) [PSYoungGen: 25764K->23188K(51712K)] [ParOldGen: 30275K->27887K(30720K)] 56039K->51076K(82432K), [Metaspace: 53480K->53480K(1099776K)], 0.1561581 secs] [Times: user=0.69 sys=0.00, real=0.16 secs] 
2022-03-01T11:14:25.559+0800: 453.194: [Full GC (Allocation Failure) [PSYoungGen: 23188K->23176K(51712K)] [ParOldGen: 27887K->27720K(30720K)] 51076K->50897K(82432K), [Metaspace: 53480K->53457K(1099776K)], 0.2252083 secs] [Times: user=0.91 sys=0.00, real=0.22 secs] 
2022-03-01T11:14:25.986+0800: 453.621: [Full GC (Ergonomics) [PSYoungGen: 30028K->23978K(51712K)] [ParOldGen: 27720K->27661K(30720K)] 57749K->51639K(82432K), [Metaspace: 53928K->53928K(1099776K)], 0.2050496 secs] [Times: user=0.70 sys=0.03, real=0.20 secs] 

说明启用了Parallel Old这个老年代收集器。

使用Jconsole连接该程序,切换到VM概要这个tab,注意下图红圈圈出来的地方:

image-20220301114514944

JVM参数 显式使用-XX:+UseSerialGC

-Xms100M
-Xmx100M
-Xmn70M
-XX:PretenureSizeThreshold=5M
-client
-XX:+UseSerialGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintGCDateStamps
-XX:+PrintGCCause
-Xloggc:gc-old.log
-verbose:gc

gc日志如下:

Java HotSpot(TM) 64-Bit Server VM (25.121-b13) for windows-amd64 JRE (1.8.0_121-b13), built on Dec 12 2016 18:21:36 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 16601588k(5142084k free), swap 26038772k(4551612k free)
CommandLine flags: -XX:-BytecodeVerificationLocal -XX:-BytecodeVerificationRemote -XX:InitialHeapSize=104857600 -XX:+ManagementServer -XX:MaxHeapSize=104857600 -XX:MaxNewSize=73400320 -XX:NewSize=73400320 -XX:PretenureSizeThreshold=5242880 -XX:+PrintGC -XX:+PrintGCCause -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:TieredStopAtLevel=1 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC 
2022-03-01T11:23:14.044+0800: 1.617: [GC (Allocation Failure) 2022-03-01T11:23:14.044+0800: 1.618: [DefNew: 57344K->5487K(64512K), 0.0091428 secs] 57344K->5487K(95232K), 0.0093811 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
2022-03-01T11:23:14.527+0800: 2.100: [GC (Allocation Failure) 2022-03-01T11:23:14.527+0800: 2.100: [DefNew: 62831K->5807K(64512K), 0.0139393 secs] 62831K->8566K(95232K), 0.0139920 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
2022-03-01T11:23:14.643+0800: 2.216: [Full GC (Metadata GC Threshold) 2022-03-01T11:23:14.643+0800: 2.216: [Tenured: 2758K->6966K(30720K), 0.0211325 secs] 28297K->6966K(95232K), [Metaspace: 20450K->20450K(1067008K)], 0.0212119 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 

此时,新生代是DefNew,老年代是Tenured,明显和前面使用了 -XX:+UseParallelGC时候不一样。

Jsonsole

引用

JVM经典垃圾回收器之 serial 收集器 和 serialOld 收集器

查看JVM使用的默认的垃圾收集器


文章作者: WangQingLei
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 WangQingLei !
  目录