【JVM】GC

什么是GC

随着程序的运行,内存中的变量,对象等越来越多轻则影响性能重则导致系统崩溃。

内存回收回收的是哪些区域呢,是堆和方法区;栈帧、程序计数器、本地方法栈的生命周期是与线程同步的,线程结束它们也就消失了,所以这些区域不需要回收。

GC就是找到存活对象回收无用对象占用的内存空间让其他对象。

什么是垃圾

要想进行垃圾回收首先得知道什么是垃圾,对程序来说判断垃圾是通过一些算法进行判断的,有以下算法

引用计数法

堆中每个对象都有一个引用计数器,每引用一次就会加一,引用失效就减一,引用为0就可以当做垃圾。
优点:执行简单,判定效率高
确定:难以检测对象循环引用。

现在的jvm中一般不使用这种算法。

根搜索算法

从GC Roots根结点往下搜索,如果对象搜索不到的时候就可以被回收了。

在这里插入图片描述
GC Roots节点有:栈中引用的对象、方法区中静态属性引用的对象、方法区中常量引用的对象、本地方法栈中引用的对象

强引用:new出来的对象都是强引用,GC无论如何都不会回收,即使抛出OOM异常。
软引用:只有当JVM内存不足时才会被回收。
弱引用:只要GC,就会立马回收,不管内存是否充足。
虚引用:可以忽略不计,JVM完全不会在乎虚引用,你可以理解为它是来凑数的,凑够"四大天王"。它唯一的作用就是做一些跟踪记录,辅助finalize函数的使用。

内存分区

在这里插入图片描述

  • 新生代

新生代分为三个部分Eden、s0、s1,几乎所有新生对象都放在年轻代,大部分对象在Eden区生成,Eden满了的时候会发生一次GC,回收时将Eden中存活的对象复制到s0去,然后清空Eden,s0如果满了就把s0和Eden中的对象复制到s1区,然后s0s1互换,循环往复。如果s1 不能放下s0 和 Eden区的存活对象则把存活对象复制到老年代。如果在s区躲过一次GC年龄加一,默认年龄达到15岁自动放入老年代。若老年代满了则出发GullGC,也就是新生代老年代都回收。

  • 老年代

在年轻代存活N代后仍然存活的对象,生命周期较长的对象都放在老年代。内存比新生代大,老年代满了的时候发生FullGC,但是频率较低,一般情况下,大对象(内存连续的大数组)会直接分配到老年代。

  • 持久代

用来存放class类、方法和常量等信息。一般回收的是无用的类和常量。

对象优先在Eden分配
大对象直接进入老年代
长期存活的对象进入老年代

新生代GC比较频繁,会有大量对象死去,可以选择复制算法完成收集
老年代GC频率低,但是速度慢,可以使用标记清除算法或标记压缩算法
新生代采用空闲指针方式控制GC触发,有新对象要分配内存时用来检查空间是否足够,不够就GC;连续分配的时候对象从Eden到S区再到老年代

GC算法

复制算法

通过根搜索算法找到存活对象,将存活的对象复制到一块空闲区域
将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收

在这里插入图片描述

优点:比标记清除算法效率高
限制:不适用于存活对象较多的场合,如老年代

标记-清除

标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。
在这里插入图片描述
标记阶段需要全盘扫描这个过程比较耗时
在这里插入图片描述
清除阶段是清除没有引用的对象,存活的保留,清除不需要移动对象,仅仅清理不存活的对象,会有碎片产生。

标记-压缩

标记压缩与标记清除的区别是,清除之后把存活对象放到一起,这样可以减少碎片。
在这里插入图片描述

总结

少量存活对象,复制算法
大量存活对象,标记清理或标记压缩

可触及性
可触及的:从根节点可以触及到这个对象
可复活的:一旦所有引用被释放就是可复活状态,因为在finalize()中可能复活该对象
不可触及的:在finalize()后,可能会进入不可触及状态,这种对象不可能复活,可以被回收。

Stop-The-World
java中一种全局暂停的现象,所有java代码停止执行,native代码可以执行但是不能和jvm交互,大部分情况是由GC引起。
为什么停顿:在回收的时候仍然有垃圾产生垃圾会永远清理不完。
危害:长时间服务停止,没有响应;高可用系统中可能会引起主备切换严重影响生产环境。

垃圾收集器

串行收集器

采用单线程进行回收,收集时需要对所有正在执行的线程暂停,适用于单核处理器的系统。新生代老年代使用串行收集器,新生代复制算法,老年代标记压缩算法。-XX:+UseSerialGC
在这里插入图片描述

并行收集器

与串行相比引入多线程进行收集。

-XX:+UseParNewGC
新生代并行
老年代串行
Serial收集器新生代的并行版本
复制算法
多线程,需要多核支持
-XX:ParallelGCThreads 限制线程数量

在这里插入图片描述

Parallel收集器
类似ParNew
新生代复制算法
老年代 标记-压缩
更加关注吞吐量
-XX:+UseParallelGC 
使用Parallel收集器+ 老年代串行
-XX:+UseParallelOldGC
使用Parallel收集器+ 并行老年代

-XX:MaxGCPauseMills
最大停顿时间,单位毫秒
GC尽力保证回收时间不超过设定值
-XX:GCTimeRatio
0-100的取值范围
垃圾收集时间占总时间的比
默认99,即最大允许1%时间做GC

CMS收集器

与用户线程一起执行的标记清除收集器,并发阶段会降低吞吐量,适用于老年代。
-XX:+UseConcMarkSweepGC
标记阶段

  • 初始标记
    根可以直接关联到的对象
    速度快
  • 并发标记(和用户线程一起)
    主要标记过程,标记全部对象
  • 重新标记
    由于并发标记时,用户线程依然运行,因此在正式清理前,再做修正
  • 并发清除(和用户线程一起)
    基于标记结果,直接清理对象
    在这里插入图片描述

特点:尽可能降低了停顿,但是会影响性能和吞吐量,清理不彻底(边收集边清理),不能在空间快满时清理。

GC参数整理

-XX:+UseSerialGC:在新生代和老年代使用串行收集器
-XX:SurvivorRatio:设置eden区大小和survivior区大小的比例
-XX:NewRatio:新生代和老年代的比
-XX:+UseParNewGC:在新生代使用并行收集器
-XX:+UseParallelGC :新生代使用并行回收收集器
-XX:+UseParallelOldGC:老年代使用并行回收收集器
-XX:ParallelGCThreads:设置用于垃圾回收的线程数
-XX:+UseConcMarkSweepGC:新生代使用并行收集器,老年代使用CMS+串行收集器
-XX:ParallelCMSThreads:设定CMS的线程数量
-XX:CMSInitiatingOccupancyFraction:设置CMS收集器在老年代空间被使用多少后触发
-XX:+UseCMSCompactAtFullCollection:设置CMS收集器在完成垃圾收集后是否要进行一次内存碎片的整理
-XX:CMSFullGCsBeforeCompaction:设定进行多少次CMS垃圾回收后,进行一次内存压缩
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收
-XX:CMSInitiatingPermOccupancyFraction:当永久区占用率达到这一百分比时,启动CMS回收
-XX:UseCMSInitiatingOccupancyOnly:表示只在到达阀值的时候,才进行CMS回收

参考博客:https://www.jianshu.com/p/5261a62e4d29
https://juejin.im/post/5a15be736fb9a044fc4464d6#heading-8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值