技.艺.道:认识GC垃圾回收机制

一、什么是垃圾

垃圾就是无用的东西。那怎么区分一个东西有用无用呢?

1.垃圾的判定

1.1 引用计数法

原理:

最初人们的想法是这样:一个东西,我拿来用一次就记作+1,用好了放回去就记作-1,如果一个东西最后在盘算的时候,发现它的得分是0,那么就可以认为它目前没有被谁拿去用,那我现在把它丢掉不会影响到任何人现在正在做的事情。想法是不是很好?

问题:

可是,如果有一天,我们有一个螺丝钉,一个螺丝帽。我们取出螺丝钉和螺丝帽,并把螺丝帽和螺丝钉拧在一起,拧成一个螺组。虽然这个螺组我们没有在用了,可是由于螺丝钉在和螺丝帽配对,螺丝帽也在忙着和螺丝钉配对,因此他俩都不能被放回去。使得这两个工具无意义的占用着空间,因为他们的组合体现在实际上并没有什么用。

使用场景:

据说Lisp,Python,Ruby等的垃圾收集算法采用的就是引用计数算法。

1.2 可达性分析算法

原理:找不在葡萄串上的葡萄,只需要捏着一串葡萄的根部提起来,落在盘子里的葡萄就是不在葡萄串上的葡萄了。这就是可达性分析算法的原理。即:先找到所谓的根节点,从跟出发,所有用到的都不能回收,因为都有用,其他的用不着的都可以回收。

问题:

暂未明确

使用场景:

JVM中使用的通常都是可达性分析算法来判断哪些是垃圾对象。

细说:

jvm扫描堆中的对象,看是否能够沿着GCRoot对象为起点的引用链找到该对象,找不到,表示可以回收,反之则保留。在实际的操作中通常是

可以通过jmap 工具抓取程序当今的堆内存使用快照
jmap -dump:format=b,live,file=1.bin pid
表示通过jmap 抓取堆内存快照,格式是2进制格式(format=b);
live:表示只抓取存活的对象而忽略掉已经被回收的对象。
file=1.bin :表示抓取的文件会存放在1.bin这个文件中。
通过Eclipse Memory Analyzer工具打开抓取的文件,在java basics->GC Roots中查看可以作为root的 对象。
1.System Class 系统核心类
2.Native Stack 原生类
3.Thread 线程相关:活动线程。线程的每次方法调用产生的栈帧都可以作为方法对象。
4.Busy Monitor 正在加锁的对象

“可达性分析算法”就是看堆中的每个对象是不是在根节点的引用链上。如果在根节点上,就不能被回收,否则可以。

“引用链”即通过引用关系产生的链状结构。

关于“引用”:

引用可不单是 A a = new A();

这个叫做“强引用”。其他引用还有软引用、弱引用、虚引用、终接器引用。

【后面我单独写一篇来讲引用。】

这里只做简单的说明。

  • 被GC Roots对象强引用的对象不会被回收;
  • 被GC Roots对象软引用的对象在发生了GC和GC后内存仍然不足时它会被垃圾回收;
  • 被GC Roots对象弱引用的对象在发生了GC时它会被垃圾回收;

应用:当内存紧张时,可以使用软链接。

 

二、垃圾回收发生在哪里

开门见山,这里讲jvm内存结构

堆内存中,分为新生代,老年代和StringTable(串表区)。其中新生代分为伊甸园区、幸存区From、幸存区To。

通常情况下,对象在伊甸园诞生,随着一轮小规模垃圾回收,一些有用的对象就会被复制到“幸存区To”,这些被复制过来的对象的年龄会加一,而伊甸园区则会整个被回收,当这个过程结束之后,幸存区To就会与幸存区From交换名字,即:此时幸存区To变为了空的,对象都到了幸存区From。等到下次小规模回收的时候,幸存区From和伊甸园里的有用对象年龄会再统一加一,年龄达到老年标准的对象可以直接进入老年代,没有达到的会被放入幸存区To,然后幸存区To和幸存区From再交换名称。

意外情况:

天神下凡:当新生区连续内存空间不足以存放一个大对象而老年代存得下时,对象会在老年代被创建。

补充说明:

StringTable:串池,当创建一个字符串变量时,会先从串池中搜索是否存在该字符串,若有,则直接引用,若没有,则创建该字符串并将其添加到串池,并引用。它的本质是一个HashTable,大小是固定的不可扩容。

三、垃圾回收的种类

上面提到的“小规模垃圾回收”实际上叫做“minor GC”。

1.Minor GC

  • 负责从新生代进行垃圾回收。
  • 新对象总是在伊甸园产生的(当新生代连续空间不足时,大对象可以在老年代被创建)
  • 当新生代内存不足时,触发minorGC,将伊甸园中的有用对象复制到幸存区To年龄+1,并将幸存区To与幸存区From互换名称,接着将整个伊甸园清理掉。
  • 当第二次minorGC时,会将伊甸园和幸存区From中的有用元素都放入幸存区To,对象年龄+1,然后清空伊甸园和幸存区From,再将To和From互换名称。
  • 随着一次次的minorGC,当对象的年龄到15岁时(最大),会被移动到老年代中。对象的寿命最大为15,因为对象头用来存放年龄的位置为4bit。当新生代空间不足时会将一些对象放入老年代。
  • minor GC发生时,会引发stop the world,暂停用户线程,等GC结束之后用户线程才会恢复运行。
  • 当老年代空间不足,会先尝试触发minorGC,如果之后空间仍然不足,会触发一次full GC。它同样会引起一次 stop the world,而且stw时间更长。

2.Full GC

  • 在老年代空间不足时负责从老年代进行垃圾回收。

3.小结

  • 新生代内存不足发生的垃圾收集都是 minor GC
  • Serial GC和Parallel GC在老年代内存不足时发生的垃圾收集都是 Full GC
  • 在CMS和G1中,当老年代内存不足时,当老年代内存占堆内存45%,进入并发标记阶段,进而进入混合收集阶段。在这两个阶段中,若回收速度大于垃圾产生速度,则不会产生Full GC;若回收速度小于垃圾产生速度,即垃圾收集不过来,则会退化为并行收集,进而发生Full GC。

三、如何清理

1.标记清除算法

清理垃圾的方式有很多种,比如自己准备一些便利贴,哪些东西没用了你就给它贴一张写着
“垃圾”的便利贴,等到周末有空了,把所有贴了“垃圾”便利贴的东西都给扔到垃圾箱去就可以了(记得自己在扔的时候分个类啊!不然可能会罚款)这就是标记清除算法。

2.标记整理算法

当然你也可以在标记之后,把这些垃圾尽可能的放在一块,这样的好处就是你的空间会集中一些,不至于这里一点空,那里一点空的。这就是标记整理算法。

3.复制算法

当你家足够大,或者是你的垃圾足够多的,钱也足够多,那你就不用像上面那么做了,可以直接把有用的东西再买一份放到另一间空屋子就可以了。这就是复制算法。

四、有哪些垃圾回收器

按原理分

1.串行 垃圾回收器

特点:

  • 单线程
  • 适用于堆内存较小,适合个人电脑

Serial:新生代的垃圾回收器,使用“复制”算法回收垃圾。

SerialOld:老年代的垃圾回收器,使用“标记整理”算法回收垃圾。

 

2.吞吐量优先 垃圾回收器

特点:

  • 多线程
  • 堆内存较大,多核cpu
  • 让单位时间内,STW的时间最短 如:0.2+0.2=0.4(STW:stop the world,即当执行到垃圾回收的某个阶段时,会将所有用户线程暂停下来,这段暂停时间被称为SWT)

开启开关:-XX:+UseParallelGC~-XX:+UseParallelOldGC~(在jdk1.8开始,此开关默认开启。即默认使用吞吐量优先垃圾回收器)

工作模式:-XX:+UseAdaptiveSizePolicy:自适应调整新生代的大小

GC时间占比:-XX:GCTimeRatio = ratio:用来设定GC时间与程序运行时间的比值。1/(1+ratio);默认是99,通常需要配置成19.

最大暂停毫秒数:-XX:MaxGCPauseMillis=ms:默认值是200;

3.响应时间优先 垃圾回收器

 

并行标记清除 垃圾回收器

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~SerialOld

-XX:+UseConcMarkSweepGC :用来进行老年代垃圾回收;当其并发失败,切换至SerialOld进行垃圾回收

-XX:+UseParNewGC:工作在新生代的垃圾回收器

并行度与并发数:

-XX:ParallelGCThreads=n~-XX:ConcGCThreads=threads

并行的垃圾回收线程数:通常等于cpu核数。

并发的垃圾回收线程数:通常设置为并行数的1/4。即:留着剩余3/4的线程数用来执行用户逻辑。

并行数是垃圾回收线程能使用的最多线程数,并发数是可以同时运行的垃圾回收器线程数。

执行cms内存回收时的内存占比:-XX:CMSInitiatingOccupancyFration=percent

重新标记前对新生代进行垃圾回收:-XX:+CMSScavengeBeforeRemark

cms存在由碎片过多导致的同步失败,会使垃圾收集时间剧增。这是其一大问题。

按实现分

CMS垃圾回收器

CMS(Concurrent Mark Sweep),是一款并发的、使用标记-清除算法的垃圾回收器。

具体说明可以看这篇https://www.jianshu.com/p/2a1b2f17d3e4

G1垃圾回收器

  • 同时注重吞吐量和低延迟,默认的暂停目标是200ms
  • 支持超大堆空间,会将堆划分为多个大小相等的Region
  • 整体上是标记+整理算法,两个区域之间是复制算法
  • 这是一种在jdk9中默认使用的垃圾回收器。下面有一篇详细说明。

https://www.jianshu.com/p/aef0f4765098

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章