GC垃圾回收

一、垃圾

在c或c++中垃圾是要手动进行回收的

Java:new对象申请内存

      自动回收内存,若手动释放内存,容易出现两种类型问题:

    1、忘记回收,导致内存溢出

    2、多次回收

二、什么是垃圾

1、如果一个对象没有任何引用指向他

2、存在多个对象,互相之间引用,没有其他对象指向这个循环引用的对象(多个)

三、如何定位垃圾

1、引用计数法:一个对象有几个引用,这个对象中的计数器为相应的数,当计数器为0时,为垃圾(不能解决循环引用的问题)

但是,当三个对象循环引用,每个对象的计数器都为1,此时没有对象引用该整体时,会发生内存泄漏

 2、根可达算法:程序会找到一些根对象,与根对象有关联的都不是垃圾,其余都是垃圾

 四、垃圾回收算法

1、标记清除:找到垃圾后清除,相应区域标记为非垃圾区域

该算法适合存活对象较多的情况

存在问题:位置不连续、产生碎片

 黑色为垃圾,绿色为未使用

2、拷贝:将内存区域非为两半,在上半区找到正在使用的内存,拷贝到下半区。拷贝完成后将上半区清空,标记为未使用区域,需要内存时往下半区分配

该算法适合伊甸区

存在问题:浪费空间,需要准备两份。项目中需要5G,你要准备10G内存

3、标记压缩:从头开始按照顺序,将存活对象拷贝到可回收区域,形成连续空间

好处:没有碎片,内存连续。但是效率偏低

效率偏低原因:若单线程操作,效率本身就低

       若多线程操作,需要线程同步

 五、JVM内存分代模型

1、new出来的对象尝试往栈上扔,栈放不下,判断对象大不大,如果大扔到老年代,通过fgc清除,如果不大扔到伊甸区,通过s0s1后ygc清除

  在YGC期间如果survivor区空间不够,通过空间担保直接进入老年代

2、新生代+老年代+永久代(1.7)/元数据区(1.8)

  永久代、元数据都是装Class对象中

  永久代必须指定大小限制,元数据区可以设置也可以不设置,若不设置最大为物理内存

4、字符串常量:在1.7前放在方法区中,在1.8之后放在堆中

3、堆内存逻辑分区

新生代=伊甸+2个survivor区 

  ①YGC回收之后,大多数内存会被回收,存活的对象进入S0,伊甸园区清空

  ②伊甸+S0中存活的对象拷贝到S1中,其余两个区域清空

  ③再次YGC,伊甸+S1中存活对象放到S0中

  ④当对象年龄达到最大15(在对象头中用4位记录gc次数)时或(Eden区+S0区)放入S1后超过S1的50%,将年龄最大的对象放到老年代中

老年代满之后会触发FGC(Full GC)

4、调优目标:尽量减少FGC

 默认新生代:老年代=1:2

新生代中伊甸:survivor:survivor = 8:1:1

六、常见的垃圾回收器

1、Serial+Serial Old

Serial(年轻代,串行回收,单线程):单线程,并行回收,垃圾回收线程和工作线程交替执行

Serial Old:单线程在老年代

2、Paraller Scavenge+ParallelOld

Paraller Scavenge(年轻代,并行回收):多线程清理垃圾

ParallelOld:将Parallel放到old区

3、ParNew+CMS

ParNew(年轻代):配合CMS的并行回收

CMS(老年代):在进行垃圾回收的时候程序也可以运行,即并发回收,效率较高,降低STW的时间(200ms)

        STW:在gc过程中,会产生应用程序的卡顿,这个停顿称为STW

 从线程角度理解CMS:

  初始标记:根据根可达算法找出第一层根(线程栈变量、静态变量、常量池和JNI指针)。此阶段是STW的

  并发标记:根据初始标记继续向下寻找垃圾。使用三色标记算法

        

  重新标记:在并发标记中标记的垃圾可能被引用或者之前被引用的对象引用消失,此时需要重新标记

  并发清理:多线程并发清理垃圾

 4、G1:将内存分为一小块一小块的内存空间,在a小块内存玩时候,同时回收其他小块的内存空间

如果G1产生FGC,你应该怎么做?  G1也会发生FGC,可以降低MixedGC的阈值,让MixedGC提前发生

三个标记中G1用SATB

5、ZGC

6、三色标记:已经遍历过自己以及下一级对象的标记为黑色,标记完自己但未标记下一级对象的标记为灰色,未标记自己的标记为白色

  三色标记会出现漏标(本来是可用对象,但是由于没有遍历到被当成垃圾回收掉了)的情况,从上图转换为下图。在标记过程中B->D的引用消失,A新增引用指向D

  

  解决漏标的办法:①将A对象重新标记为灰色     ②SATB: 当B->D的引用消失时,将D对象推到GC堆栈中保证下次GC时可以扫描到

  在G1垃圾回收中使用了第二种解决办法,因为第一种会将A对象下所有的引用重新扫描再次进行标记

 

posted @ 2021-05-09 16:18  showMeTheCodes  阅读(89)  评论(0编辑  收藏  举报