Welcome everyone

GC的前世今生

java 汪明鑫 1099浏览 0评论

GC前言

java与C++之间又一堵由内存动态分配和垃圾收集技术所围成的“高墙”,墙外面的人想进去,墙里面的人想出来。

——–<<深入理解java虚拟机>>

 

C/C++ 中主要由用户程序代码来回收分配的内存,不存在无用的对象的筛选过程,效率上比垃圾回收机制更胜一筹。

java语言的一些性能上的劣势都是为了换取开发效率。

 

 

首先,了解下“stop the world”

stop the world会在执行某一个垃圾回收算法的时候产生,JVM为了执行垃圾回收,会暂停java应用程序的执行,等垃圾回收完成后,再继续运行。

java程序有不规则的停顿现象,其实这就是“stop the world”,停顿的时候JVM是在做垃圾回收。所以尽可能减少stop the world的时间,就是我们优化JVM的主要目标。

 

回收靠谁来做?  —– > GC

什么是GC?

Garbage Collection  垃圾收集(顾名思义,回收垃圾,不需要的对象)

 

GC要完成的事情:

1,哪些内存需要回收

2,什么时候回收

3,如何回收

 

我们需要关注的事情:

1,GC作用域

2,GC算法和回收器

3,GC日志

3,优化GC

 

 

GC关注的是堆,我们就要先了解堆

注:1.8废弃了永久代  产生元空间(Metaspace)

 

 

 

可达性分析

无用的对象回收,有用的对象存活,那么如何判断对象是否存活?

 

如上图:

GC ROOTS 到object1 ,object2,object3,object4有通路,即对象可达,则对象存活

GC ROOTS 到object5 ,object6,object7没有通路,即对象不可达,则判定为可回收的对象

 

在 Java 中 GC Roots 一般包含以下内容:

  • 虚拟机栈中引用的对象
  • 本地方法栈中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中的常量引用的对象

 

 

垃圾收集算法

1,标记-清除算法

 

 

出现的最早

什么叫标记,怎么标记 ?    —– >  通过GC ROOTS的可达性分析对需要回收的对象做标记

什么叫清除?                         —– >  统一清除(回收)所有被标记的对象

 

不足之处

效率问题 :两次扫描遍历耗时严重,标记和清除两个过程本身的效率也都不高

空间问题:标记清除后会产生大量不连续的内存碎片

 

2,标记-整理算法

 

3,复制算法

为了解决效率问题,复制算法横空出世

实现简单 运行高效 内存空间利用率低

 

 

[分代收集算法]
新生代:复制算法

老年代:标记-清除算法    标记-整理算法

把堆分为新生代和老年代 ,这样就可以根据各个年代的特点采用最适当的收集算法。

 

设计时,对象尽量不要进入老年代 ,而是使我们的对象尽量在新生代回收掉(最理想的状态下,所有的对象都不要进入老年代)

新生代 minor GC

老年代 major GC 比新生代慢十倍

full GC是minor GC和major GC的总称

 

 

垃圾收集算法和垃圾收集器有什么关系?

垃圾收集算法是理论,垃圾收集器是垃圾收集算法的实现。

 

垃圾收集器

垃圾收集器之间的连线表示搭配使用

目前流行的是ParNew 和 CMS的搭配使用

 

Serial      Serial Old

串行回收器

应用程序暂停 ,GC线程运行过程中使用单线程进行串行回收

 

ParNew  

应用程序暂停 ,GC线程多线程并发回收

可以指定并发的线程数

 

Parallel Scavenge         Parallel Old

重视吞吐量

应用程序暂停 ,GC线程多线程并发回收

 

CMS

Concurrent Mark Sweep,并发标记清除。

 

标记的流程:

        1, 初始标记

标记从GC Root可以直接可达的对象;

         2,并发标记(和应用程序线程一起)

主要标记过程,标记全部对象;

 3,重新标记

由于并发标记时,用户线程依然运行,因此在正式清理前,再做依次重新标记,进行修正。

   4,并发清除(和用户线程一起)

基于标记结果,直接清理对象。

 

并发标记是最主要的标记过程,而这个过程是并发执行的,可以与应用程序线程同时进行,初始标记和重新标记虽然不能和应用程序并发执行,但这两个过程标记速度快,时间短,所以对应用程序不会产生太大的影响。最后并发清除的过程,也是和应用程序同时进行的,避免了应用程序的停顿。

 

CMS的优点显而易见,就是减少了应用程序的停顿时间,让回收线程和应用程序线程可以并发执行。但它也不是完美的,从他的运行机制可以看出,因为它不像其他回收器一样集中一段时间对垃圾进行回收,并且在回收时应用程序还是运行,因此它的回收并不彻底。这也导致了CMS回收的频率相较其他回收器要高,频繁的回收将影响应用程序的吞吐量。

 

G1

Garbage First

G1将堆空间划分成了互相独立的区块。

第一时间处理垃圾最多的区块。

 

优点

1、因为划分了很多区块,回收时减小了内存碎片的产生;

2、G1适用于新生代和老年代,而CMS只适用于老年代。

 

通过引入 Region 的概念,从而将原来的一整块内存空间划分成多个的小空间,使得每个小空间可以单独进行垃圾回收。这种划分方法带来了很大的灵活性,使得可预测的停顿时间模型成为可能。通过记录每个 Region 垃圾回收时间以及回收所获得的空间(这两个值是通过过去回收的经验获得),并维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。

 

 

内存分配策略

GC是回收对象,那对象的创建和内存的分配是怎样一个过程?

虚拟机为新生对象分配内存,对象所需内存的大小在类加载完成后便可以完全确定

 

分配内存时有两种情况:

1)java堆中的内存是规整的

什么叫规整 —–>  所有用过的内存放在一边,空闲的内存放在另一边,中间放着指针作为分界点的指示器

上述分配方法称   “指针碰撞”
指针可以看作是一个内存空间地址的偏移量

 

2)java堆中的内存不是规整的

已使用的内存和空闲的内存相互交错,必须维护一个列表,记录哪些内存块可以用

在分配时从列表中找到一块足够大的空间划分给对象,并更新列表上的记录。

上述分配方式称“空闲链表”(Free List)

 

 

 

对象优先在Eden区分配

Eden区空间不够,触发Minor GC(针对新生代的GC)

 

大对象直接进入老年代

-XX:PretenureSizeThreshold=3145728 3M

上述jvm参数 表示有超过3MB的对象会直接在老年代分配

 

长期存活的对象直接进入老年代

对象如果从Eden区经过一次Minor GC到Survivor区,age=1

在Survivor区每熬过一次Minor GC,age++

设置晋升老年代年龄阈值

-XX:MaxTenuringThreshold=15

age=15时,进入老年代

 

动态对象的年龄判定

相同年龄所有对象的大小总和 > Survivor空间的一半

年龄大于或等于该年龄的对象就可以直接进入老年代,无需考虑MaxTenuringThreshold设置的阈值

 

空间分配担保

Minor GC 之前检查 老年代最大可用连续空间是否>新生代所有对象总空间

 

 

参考文章&学习资料

https://www.cnblogs.com/leefreeman/category/1058724.html

https://www.pdai.tech/md/java/jvm/java-jvm-gc.html

http://wearygods.online/jvm-base

 

 

转载请注明:汪明鑫的个人博客 » GC的前世今生

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz