JDK之垃圾回收

文章目录

1.概述

1.我们前面的讲过了JAVA对象的内存模型,以及运行时数据区的相关内容,但是随着对象的不断地增多,我们内存大小如何有效保持一个
  可用的状态,可能是比较重要的。
2.也就是我们如何将无效的对象清理掉(垃圾回收掉)显得异常重要,这个章节的内容主要是讲述这方面的知识;  
3.因为我们知道,堆内存主要是存储的对象或者数组,因此

在这里插入图片描述

1.2.关于对象的垃圾回收问题

在这里插入图片描述

1.我们知道,关于对象的创建,都是由于线程的创建导致新的对象的创建,我们又知道,关于对象对象存储都是在堆内存中的,
  所以一旦线程结束了,我们堆内存中的对象实际上就可以被清理了,我们这里的垃圾回收主要是侧重于对堆内存的回收,
  因为方法区存储的一般都是,应用启动的时候就存在的一些类信息等相关内容,并不是说方法区等没有回收,只是这里不是我们重点要讨论的对象;

2.如何确定对象是否可以被回收

1.要想回收某个对象,那么就需要判断这个对象是不是垃圾,那么如何判断?
  下面两种方案:

2.1.方案一:引用计数法

2.1.1.概念
1.所谓引用计数法:就是说如果一个对象不被引用,我们就认为是不被使用,意味这个这个对象是可以被回收的;
2.1.2.弊端
1.如果两个对象互相引用,那么就意味着两个对象永久性的无法被回收?对象无法被回收,显然这种方法时不行的

2.2.方案二:可达性分析

2.2.1.概念

1.通过可以作为GC ROOT的对象(或者叫工具),去向下寻找,看看是否可以找到我们正在验证的对象;

2.2.2.可以作为GC ROOT的对象工具标示

2.2.2.1.Class

Class - 由系统类加载器(system class loader)加载的对象,这些类是不能够被回收的,他们可以以静态字段的方式保存持有其它对象。我们需要注意的一点就是,通过用户自定义的类加载器加载的类,除非相应的java.lang.Class实例以其它的某种(或多种)方式成为roots,否则它们并不是roots,.

2.2.2.2.Thread

Thread - 活着的线程

2.2.2.3.Stack Local JVM栈本地变量表中的变量

Stack Local - Java方法的local变量或参数

2.2.2.4.其他
JNI Local - JNI方法的local变量或参数
JNI Global - 全局JNI引用
Monitor Used - 用于同步的监控对象
Held by JVM - 用于JVM特殊目的由GC保留的对象,但实际上这个与JVM的实现是有关的。可能已知的一些类型是:系统类加载器、一些JVM知道的重要的异常类、一些用于处理异常的预分配对象以及一些自定义的类加载器等。然而,JVM并没有为这些对象提供其它的信息,因此就只有留给分析分员去确定哪些是属于"JVM持有"的了。

3.垃圾回收的算法

3.1.标记清除算法-[marked-sweep]

3.1.1.标记

1.遍历堆内存中的所有对象,首先需要标记对象的类型(Live Object,Unuse,UnReference)
2.这种回收算法:因为需要遍历所有的对象,所以是比较耗时的;

在这里插入图片描述

3.1.2.清除

在这里插入图片描述

3.1.2.弊端

标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程
序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
(1)标记和清除两个过程都比较耗时,效率不高
(2)会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存
   而不得不提前触发另一次垃圾收集动作。

3.2.复制 (Copy)

3.2.1.概念

1.将内存分为两块,每次我们只使用一块,如图:

在这里插入图片描述

当其中一半的内存全部被使用完了之后,就会把LiveObject 对象移动到另外一半上,其他的被回收掉,回收复制之后如下图
在这里插入图片描述

3.2.2.弊端

1.因为一直有一半的内存需要空闲,需要牺牲一半的内存,空间利用率比较低

3.3.标记整理(Mark Compact)

1.标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,
  然后直接清理掉端边界以外的内存。

在这里插入图片描述
让所有存活的对象都向一端移动,清理掉边界意外的内存。
在这里插入图片描述

3.4.总结

既然上面介绍了3中垃圾收集算法,那么在堆内存中到底用哪一个呢?
Young区:复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
Old区:标记清除或标记整理(Old区对象存活时间比较长,复制来复制去没必要,不如做个标记再清理)

在这里插入图片描述

3.4.1.Young区使用复制算法

1.因为我们知道对象的创建是基于线程的创建而出现的,但是线程来讲的话,大多数的线程时间是非常短的(转瞬即逝),一旦线程结束,对应着堆内存Young区的
  对象大部分实际上是可以被回收,也就是说Eden区的对象大多数是可以直接被回收掉的,那么不能被回收掉的对象,那么在GC的时候会被放到S1区,这个时候
  Eden区剩余的对象我们就可以直接被回收(这里注意下:是先进行复制,然后进行清理掉垃圾对象)
2.也就是:复制算法实际上适用用少量存活对象的区域清理;  

在这里插入图片描述

3.4.2.Old区使用标记清除或标记整理算法

1.我们知道Old区(老年代)的对象,一般都是被回收了好多次之后(默认15)才从Young区(新生代)进行Old区的,因此可能Old区的对象,是不太好被清理掉的
  或者说我们认为Old区的对象存活时间是比较长的,所以如果使用复制算法,那么意味着你需要不断的来回复制,这个是比较损耗效率的; 
2.所以这里使用的主要是标记清除/标记整理的算法;Old区的对象被标记之后,再次请清除/整理,相对而言,Old区的这个垃圾回收频率上没有必要那么高,
  时效性也要求不高,因为很多对象都是长时间存在的,就是没有必要那么快的将其清理掉;

5.垃圾收集器

5.1.官网

https://docs.oracle.com/javase/8/index.html
在这里插入图片描述
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/

5.2.垃圾收集器介绍

5.2.1.Serial垃圾收集器

5.2.1.1.概念
Serial收集器是最基本、发展历史最悠久的收集器,曾经(在JDK1.3.1之前)是虚拟机新生代收集的唯一选择。
它是一种单线程收集器,不仅仅意味着它只会使用一个CPU或者一条收集线程去完成垃圾收集工作,更重要的是其在进行垃圾收集的时候需要暂停其他线程。
5.2.1.2.优点,缺点,算法,适用访问,应用场景
优点:简单高效,拥有很高的单线程收集效率
缺点:收集过程需要暂停所有线程
算法:复制算法
适用范围:新生代
应用:Client模式下的默认新生代收集器
5.2.1.3.图示解析

在这里插入图片描述

5.2.2.ParNew垃圾回收器

5.2.2.1.概念
可以把这个收集器理解为Serial收集器的多线程版本。
5.2.2.2.优点,缺点,算法,适用访问,应用场景
优点:在多CPU时,比Serial效率高。
缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。 算法:复制算法
适用范围:新生代
应用:运行在Server模式下的虚拟机中首选的新生代收集器
5.2.2.3.图示解析

在这里插入图片描述

1.具体几个垃圾回收的线程,与CPU内核的数量有关系

5.2.3.Parallel Scavenge收集器

5.2.3.1.概念
Parallel   Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,
 但是Parallel Scanvenge更关注 系统的吞吐量 。

关于吞吐量的介绍

吞吐量=运行用户代码的时间/(运行用户代码的时间+垃圾收集时间)
比如虚拟机总共运行了100分钟,垃圾收集时间用了1分钟,吞吐量=(100-1)/100=99%。
若吞吐量越大,意味着垃圾收集的时间越短,则用户代码可以充分利用CPU资源,尽快完成程序的运算任务。
-XX:MaxGCPauseMillis控制最大的垃圾收集停顿时间,
-XX:GCTimeRatio直接设置吞吐量的大小。
5.2.3.2.优点,缺点,算法,适用访问,应用场景

5.2.4.Serial Old收集器收集器

5.2.4.1.概念
Serial Old收集器是Serial收集器的老年代版本,也是一个单线程收集器,不同的是采用"标记-整理算法",运行过程和Serial收集器一样。

5.2.5.CMS垃圾回收器 (并发标记清除收集器)

5.2.5.1.概念

官网地址
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector

The Concurrent Mark Sweep (CMS) collector is designed for applications that prefer shorter garbage collection pauses 
and that can afford to share processor resources with the garbage collector while the application is running. 
Typically applications that have a relatively large set of long-lived data (a large tenured generation) and run on machines with two or more processors tend to benefit from the use of this collector. However, this collector should be 
considered for any application with a low pause time requirement. The CMS collector is enabled with the command-line 
option -XX:+UseConcMarkSweepGC.

CMS(并发标记收集器)是专门为应用设计的,主要是倾向于更短的停顿时间,这样就可以跟运行中的应用共享进程的资源;
5.2.5.2.优点,缺点,算法,适用访问,应用场景
优点:由于整个过程中,并发标记和并发清除,收集器线程是可以跟业务线程是同时进行的,所以总体上来讲,CMS收集器回收线程与业务线程是一起并发的;
     并发收集,低停顿;
缺点:产生大量的碎片,并发阶段会降低吞吐量;
算法:标记 清除算法
适用范围:老年代
应用:
5.2.5.3.图示解析

在这里插入图片描述
在这里插入图片描述

1.这里注意下,我们上面整个标记清除的流程是跟业务线程是同时进行的(并发)2.我们初始标记是对所有的当前对象的标记,remark重新标记是对在整个过程中增量对象的标记;

5.2.6 Garbage-First Collector 又称G1收集器

官网

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#heap_division_by_g1

5.2.6.1.概念
1.G1是一个服务端类型的垃圾收集器;
2.满足最小的停顿时间
5.2.6.2.图示解析

在这里插入图片描述
在这里插入图片描述

1.初始标记:主要是标记GC Root 能够关联到的对象;这里注意下,初始标记会暂停用户业务线程;
2.并发标记:主要是对增量的对象标记
3.最终标记:对变化的对象进行标记
4.筛选回收:是对上面所有的region进行一个回收,这里回收是一个选择性的回收;
          这里回收时间与我们设置的(或者默认的)停顿时间有关系,比如你设置的停顿时间0.01s,那么这里回收的时间会尽可能满足;
          当然停顿时间的设置是总的G1线层的时间,比如前面标记时间用了0005s,那么这里的筛选回收旧只有0.005s,
          筛选时间如果不够,这个时候,我们去清理的Region区域就会比较少,等到下一次的G1线程再次去筛选清理;
          也就是进行选择性筛选;
5.2.6.3区域概念:Region

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#garbage_first_garbage_collection

1.逻辑上来讲,堆内存存在Eden区,Young区(包含Eden区,Survivor区);
  物理上来京,堆内存又分成了若干大小相同的区域(Region);
2.这里注意下:我们上面的标记操作,会能够识别出下面区域那些是空的;会优先收集垃圾;
3.G1收集器会把多个比较小的区域内存数据copy集中到一个region当中,在此过程中,会释放之前比较小的区域,然后压缩到新的region区域当中;

在这里插入图片描述

5.2.6.4.什么情况下,我们选择使用Garbage Frist Collector(G1)
5.2.6.4.1.堆内存使用率超过50%
5.2.6.4.2.对象分配率或提升率差异很大。
5.2.6.4.3.期望比较小的暂停时间

6.JVM调优的方面

6.1.GC 停顿时间和吞吐量

1.停顿时间:垃圾回收器进行回收执行终端进行响应的时间;
          停顿时间越小,用户体验越好,因此这个是比较适合前端这种与用户交互比较多的场景;
2.吞吐量:运行用户代码时间/(运行用户代码时间+垃圾回收时间)
         适用:用于运行代码执行时间占用资源比较大,跑任务,跑运算的
1.停顿时间比较小的:CMS,G1,另外G1可以设置停顿时间
2.

6.2.内存使用维度

7.垃圾收集器的分类

串行收集器->Serial和Serial Old
只能有一个垃圾回收线程执行,用户线程暂停。 适用于内存比较小的嵌入式设备 。

并行收集器[吞吐量优先]->Parallel Scanvenge、Parallel Old
圾收集线程并行工作,但此时用户线程仍然处于等待状态。 适用于科学计算、后台处理等若交互场

并发收集器[停顿时间优先]->CMS、G1
用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集线程在执行的 时候不会停顿用户线程的运行。 
适用于相对时间有要求的场景,比如Web 。

8.垃圾收集器选择的标准

8.1.概述

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/
在这里插入图片描述
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref28

1.运行应用之后,让JVM自己选择
2.自动选择,无法满足你的需求的话,可以按照下面的原则去做出选择;

8.2.原则

8.2.1.如果应用程序数据在100M左右,建议选择Serial GC

If the application has a small data set (up to approximately 100 MB), then
select the serial collector with the option -XX:+UseSerialGC.

8.2.2.如果应用是单线程或者对停顿时间没有要求,可以让JVM自动选择或者选择Serial GC

If the application will be run on a single processor and there are no pause time requirements, then let the VM select the collector, or select the serial collector with the option -XX:+UseSerialGC.

8.2.3.如果应用程序优先级对停顿时间没有要求,关注吞吐量,可以选择JVM自动选择或者parallel collector

If (a) peak application performance is the first priority and (b) there are no pause time requirements or pauses 
of 1 second or longer are acceptable, then let the VM select the collector, or select the parallel collector with 
-XX:+UseParallelGC.


8.2.4.如果对停顿时间有严格的要求,可以选择CMS或者G1

If response time is more important than overall throughput and garbage collection pauses must be kept shorter 
than approximately 1 second, then select the concurrent collector with -XX:+UseConcMarkSweepGC or -XX:+UseG1GC.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

东山富哥

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

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

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

打赏作者

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

抵扣说明:

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

余额充值