4、JVM垃圾回收机制、新生代的GC、GC(Minor GC、FullGC)、GC日志、JVM参数选项、元空间(笔记)

4.JVM垃圾回收机制
4.1.新生代的GC
4.1.1.串行GC(SerialGC)
4.1.2.并行回收GC(Parallel Scavenge)
4.1.3.并行GC(ParNew)
4.2.GC(Minor GC、FullGC)
4.2.1.Minor GC
4.2.2.FullGC
4.3.GC日志

4.JVM垃圾回收机制

JVM分别对新生代和老年代采用不同的垃圾回收机制。

4.1.新生代的GC

新生代通常存活时间较短,因此基于复制算法来进行回收,所谓复制算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和其中一个Survivor,复制到另一个之间Survivor空间中,然后清理掉原来就是在Eden和其中一个Survivor中的对象。新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从eden到 survivor,最后到老年代。

用java visualVM来查看,能明显观察到新生代满了后,会把对象转移到旧生代,然后清空继续装在,当旧生代也满了后,就会报OutOfMemory的异常。

在执行机制上JVM提供了串行GC(SerialGC)、并行回收GC(ParallelScavenge)和并行GC(ParNew)。

4.1.1.串行GC(SerialGC)

在整个扫描和复制过程采用单线程的方式来进行,适用于单CPU、新生代空间较小及对暂停时间要求不是非常高的应用上,是client级别默认的GC方式,可以通过-XX:+UseSerialGC来强制指定。

4.1.2.并行回收GC(Parallel Scavenge)

在整个扫描和复制过程采用多线程的方式来进行,适用于多CPU、对暂停时间要求较短的应用上,是server级别默认采用的GC方式,可用-XX:+UseParallelGC来强制指定,用-XX:ParallelGCThreads=4来指定线程数。

4.1.3.并行GC(ParNew)

与旧生代的并发GC配合使用。

旧生代的GC:
旧生代与新生代不同,对象存活的时间比较长,比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,回收后对用空出的空间要么进行合并,要么标记出来便于下次进行分配,总之就是要减少内存碎片带来的效率损耗。在执行机制上JVM提供了串行 GC(SerialMSC)、并行GC(parallelMSC)和并发GC(CMS),具体算法细节还有待进一步深入研究。

4.2.GC(Minor GC、FullGC)

GC分为两种:Minor GC、FullGC(或称为Major GC)。

4.2.1.Minor GC

从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为Minor GC。
当发生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 执行时暂停的时间将会长很多。

每次Minor GC会清理年轻代的内存。

Minor GC是发生在新生代中的垃圾收集动作,所采用的复制算法。

当一个对象被判定为”死亡”的时候,GC就有责任来回收掉这部分对象的内存空间。新生代是GC收集垃圾的频繁区域。

当对象在Eden (包括一个 Survivor 区域,这里假设是 from 区域)出生后,在经过一次Minor GC后,如果对象还存活,并且能够被另外一块Survivor区域所容纳(上面已经假设为 from 区域,这里应为to 区域,即to区域有足够的内存空间来存储Eden和from区域中存活的对象),则使用复制算法将这些仍然还存活的对象复制到另外一块Survivor区域(即to区域)中,然后清理所使用过的Eden以及Survivor区域(即from区域),并且将这些对象的年龄设置为1,以后对象在Survivor 区每熬过一次 Minor GC,就将对象的年龄+ 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold来设定),这些对象就会成为老年代
但这也不是一定的,对于一些较大的对象(即需要分配一块较大的连续内存空间)则是直接进入到老年代。

4.2.2.FullGC

Full GC是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。

现实的生活中,老年代的人通常会比新生代的人”早死”。堆内存中的老年代(Old)不同于这个老年代里面的对象几乎个个都是在Survivor区域中熬过来的,它们是不会那么容易就”死掉”了的。
因此,Full GC发生的次数不会有Minor GC那么频繁,并且做一次Full GC要比进行一次Minor GC的时间更长

另外,标记-清除算法收集垃圾的时候会产生许多的内存碎片(即不连续的内存空间),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次GC的收集动作。

4.3.GC日志

案例

package com.toto.jvm;

public class Demo {
	public static void main(String[] args) {
		Object obj = new Object();
		System.gc();
		System.out.println();
		obj = new Object();
		obj = new Object();
		System.gc();
		System.out.println();
	}
}

设置VM arguments:-verbose:gc -XX:+PrintGCDetails
在这里插入图片描述

Run之后,输出结果:
在这里插入图片描述

System.gc()可以触发Full GC

[GC (System.gc()) [PSYoungGen: 5201K->752K(151552K)] 5201K->760K(498688K), 0.0012719 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 752K->0K(151552K)] [ParOldGen: 8K->536K(347136K)] 760K->536K(498688K), [Metaspace: 2587K->2587K(1056768K)], 0.0088355 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 

[GC (System.gc()) [PSYoungGen: 2600K->96K(151552K)] 3137K->632K(498688K), 0.0005806 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 96K->0K(151552K)] [ParOldGen: 536K->535K(347136K)] 632K->535K(498688K), [Metaspace: 2588K->2588K(1056768K)], 0.0064823 secs] [Times: user=0.03 sys=0.00, real=0.02 secs] 

Heap
 PSYoungGen      total 151552K, used 2601K [0x0000000716d00000, 0x0000000721600000, 0x00000007c0000000)
  eden space 130048K, 2% used [0x0000000716d00000,0x0000000716f8a578,0x000000071ec00000)
  from space 21504K, 0% used [0x0000000720100000,0x0000000720100000,0x0000000721600000)
  to   space 21504K, 0% used [0x000000071ec00000,0x000000071ec00000,0x0000000720100000)
 ParOldGen       total 347136K, used 535K [0x00000005c4600000, 0x00000005d9900000, 0x0000000716d00000)
  object space 347136K, 0% used [0x00000005c4600000,0x00000005c4685f60,0x00000005d9900000)
 Metaspace       used 2594K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 288K, capacity 386K, committed 512K, reserved 1048576K

设置JVM参数为-verbose:gc -XX:+PrintGCDetails,使得控制台能够显示GC相关的日志信息,执行上面代码,下面是其中一次执行的结果。

jstate  -gc  -t  4235  1s

5.JVM参数选项

下面只列举其中的几个常用和容易掌握的配置选项

-Xms初始堆大小。如:-Xms256m
-Xmx最大堆大小。如:-Xmx512m
-Xmn新生代大小。通常为Xmx的1/3 或1/4。新生代 = Eden + 2个Survivor空间。实际可用空间为 = Eden + 1 个Survivor,即90%
-XssJDK1.5+每个线程堆栈大小为1M,一般来说如果栈不是很深的话,1M是绝对够用了的。
-XX:NewRatio新生代与老年代的比例,如–XX:NewRatio=2,则新生代占整个堆空间的1/3,老年代占2/3
-XX:SurvivorRatio新生代中Eden与Survivor 的比值。默认值为8。即 Eden占新生代空间的8/10,另外两个 Survivor 各占 1/10
-XX:PermSize永久代(方法区)的初始大小
-XX:MaxPermSize永久代(方法区)的最大值
-XX:+PrintGCDetails打印GC信息
-XX:+HeapDumpOnOutOfMemoryError让虚拟机在发生内存溢出时Dump出当前的内存堆转储快照,以便分析用。

-XX:Newratio: 设置Old和Yong的比例,比如值为2,则Old Generation是 Yong Generation的2倍,即Yong Generation占据内存的1/3
-XX:Newsize : 设置Yong Generation的初始值大小
-XX:Maxnewsize:设置Yong Generation的最大值大小
-XX:Surviorratio : 设置Eden和一个Suivior的比例,比如值为5,即Eden是To(S2)的比例是5,(From和To是一样大的),此时Eden占据Yong Generation的5/7

一般情况下,不允许-XX:Newratio值小于1,即Old要比Yong大。

6.元空间

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小受本地内存限制。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涂作权的博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值