参考自:https://www.cnblogs.com/csniper/p/5592593.html
https://mp.weixin.qq.com/s/ydkEkh_Uc1paftJLKIsm0w
一.判定对象为垃圾的标准
1.对象不再被引用?
对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况,当对象不再被引用时,它就会被判定为垃圾,此是该对象占据的内存就会被释放,同时此对象会被销毁。
2.如何判断对象不再被引用?
1.引用计数法
通过判断对象的引用数量来判断对象是否可以被回收,在此机制下,堆当中的每个对象都会有一个引用计数器,,当对象被创建时,若该对象的实例被分派给一个引用对象,给对象实例的计数就会被设置为1,若该对象又被另位一个对象所引用,则该对象实例的计数继续加一1+,当该对象实例的某个引用超过了生命周期,或者被设置为一个新值时,该实例的计数就会减一,-1,任何引用计数为0的对象实例,都是可以被当成垃圾回收的。
![](https://img.haomeiwen.com/i14404345/cea3e26094370c9b.png)
可以很快的执行:只需要筛选出,引用计数为0的对象,将其内存回收即可,可以交织在程序运行当中,几乎不打断程序的执行,
劣势:由于实现过于简单,可能会出现无法检测出的情况(父对象引用子对象,子对象引用父对象)
![](https://img.haomeiwen.com/i14404345/c697b1306b2c8500.png)
2.可达性分析算法
通过判断对象引用链的可达性来判断对象是否可以被回收。
可达性算法是从离散数学当中的图论引入的,程序把所有的引用关系看作一张图,
一系列的名为GC Root的对象作为起始点,从这些节点开始向下搜索,搜索走过的路经就被称为引用链,当一个对象从GC Root没有任何引用链相连,从图论上来说,就是GC Root到这个对象是不可达的,从而证明这个对象是不可用的,可回收的,这个对象就会被标记为垃圾。
![](https://img.haomeiwen.com/i14404345/75fe5ffc057bbfb0.png)
什么对象可以作为GC根呢?
1.虚拟机栈当中引用的对象:比方我们在方法里面cc=new Object();在该局部变量未被销毁之前,cc就可以称为GC Root
2.在类里面定义了一个常量,该常量保存的是某个对象的地址,被保存的地址就可以称为GC Root
![](https://img.haomeiwen.com/i14404345/f9eb77d5b4ee67f0.png)
二.谈谈你了解的GC算法
1.标记-清除算法
![](https://img.haomeiwen.com/i14404345/b32b497f6f1acc63.png)
标记:使用可达性算法找到垃圾对象进行标记,然后对堆内存从头到位进行线性遍历,若果发现有对象未被标记为可达对象,即将此对象占用的内存回收销毁对象,并且将原来标记为可达的对象的标记消除以便下一次垃圾回收。
劣势:容易造成碎片化,当要使用连续内存是可能不够。
2.标记-整理算法
![](https://img.haomeiwen.com/i14404345/8fb286b3e76fcf7e.png)
在标记清除算法的基础上进行了对象的移动,将所有的存活的对象压缩到呢村的一端,然后把末尾的其他可回收对象清除掉。
3.复制算法
![](https://img.haomeiwen.com/i14404345/a56b3d4484ae4729.png)
将可用内存按照容量分为二部分或多个块,并选中其中一个块或者2块作为对象面,其他的作为空闲面,对象是在对象面上创建的,当对象被定义为对象面的块的内存用完以后,就将还存活者的对象赋值到空闲面上,在把已使用过的内存空间一次释放掉,这种算法用与对象存活率第的场景。
![](https://img.haomeiwen.com/i14404345/9215588b611167d1.png)
4.分代收集算法
年轻代的对象存活率低,采用复制算法,老年代的采用标记算法。
![](https://img.haomeiwen.com/i14404345/e9b5f76d940956fb.png)
Minor GC:发生在年轻代当中的垃圾收集动作,所采用的是复制算法,当Eden满了的时候,会触发,年轻代计数是所以java对象出生的地方,java对象申请的内存和存放都是在这里,java当中的大多数对象不需要长久的存活,新声代是GC回收的频繁区域
Full GC:进过Minor GC之后,存活的对象会进入老年代,当老年代内存不足时会触发Full GC,其中永久代如何内存不足也会触发 fullGC
三.对象如何晋升到老年代
![](https://img.haomeiwen.com/i14404345/b07c7730c39d5e47.png)
![](https://img.haomeiwen.com/i14404345/8e65576e9147d2bb.png)
Eden:对象刚被创建出来的时候,是被存放在eden区域的,当eden区存放不下,就会被存放在Survivor区
当进行垃圾回收的时候,将,eden和一个survivor当中的存活的对象复制到另一个survivor当中(如果不够,会赋值到老年代当中),然后将前面的eden和一个survivor清空,当一次GC过后,“From”和“To”会交换他们的角色,也就是新的“To”就是 上次 GC 前的“From”,新的“From”就是上次 GC 前的“To”。
新生代和老年代是按照-Xms ,-Xmx来决定的
年龄没有达到阈值 的对象会被复制到“To”区域,经过这次 GC 后,Eden 区和 From 区已经被清 空。这个时候,“From”和“To”会交换他们
的角色,也就是新的“To”就是 上次 GC 前的“From”,新的“From”就是上次 GC 前的“To”。不管怎样,都会保 证名为 To 的 Survivor 区域是空的
1.java堆内存
![](https://img.haomeiwen.com/i14404345/8fe9817aa591f0ad.png)
java8以后,使用元空间来替换永久代,元空间使用的是本地内存,不是java虚拟机堆内存。
2.java堆内存常用调优参数
![](https://img.haomeiwen.com/i14404345/98c48761dcf75361.png)
4.Full GC
当触发老年代的垃圾回收的时候,通常也会触发新声代的的回收,以及整个堆垃圾回收,即full GC.
![](https://img.haomeiwen.com/i14404345/cfa532cdd77968ce.png)
System.gc()只是提醒JVM,JVM不一定会执行。
四.垃圾收集器
1.基本概念
![](https://img.haomeiwen.com/i14404345/350440962f52d906.png)
![](https://img.haomeiwen.com/i14404345/14d63d2eb7bd15e0.png)
2.年轻代垃圾收集器
1.Serial收集器
![](https://img.haomeiwen.com/i14404345/c6aabdf823e1a5d6.png)
2.ParNew收集器
![](https://img.haomeiwen.com/i14404345/e002898e326cf437.png)
ParNew收集器是Serial收集器新生代的多线程实现,采用多个线程对垃圾进行回收,ParNew收集器在单CPU的环境中绝对不会有比Serial收集器更好的效果,甚至由于存在线程交互的开销,可能更差。
当然,随着可以使用的CPU的数量增加,它对于GC时系统资源的利用还是很有好处的。它默认开启的收集线程数与CPU的数量相同,在CPU非常多(譬如32个,现在CPU动辄4核加超线程,服务器超过32个逻辑CPU的情况越来越多了)的环境下,可以使用-XX:ParallelGCThreads参数来限制垃圾收集的线程数。
3.Parallel Scavenge收集器
![](https://img.haomeiwen.com/i14404345/08656d483db0ae55.png)
3.老年代垃圾收集器
1.Serial old
![](https://img.haomeiwen.com/i14404345/8c39c06bbd60e7c8.png)
2.Parallel old
![](https://img.haomeiwen.com/i14404345/7277bff01624112b.png)
3.CMS收集器
![](https://img.haomeiwen.com/i14404345/196db290ea6643e3.png)
CMS:收集器
初始标记Stop-the-Word:从垃圾回收的根对象开始,只扫描能够和根对象直接关联的对象并标记,所以速度很快,虽然暂停了整个JVM,但是很快就暂停了。
并发标记:在初始标记的基础上,继续向下标记,应用程序的线程和并发标记的线程并发执行,
重新标记:扫描从根对象开始向下追述,并处理对象关联,扫描CMS堆当中剩余的对象。
它的收集过程分为四个步骤:
初始标记(initial mark)
并发标记(concurrent mark)
重新标记(remark)
并发清除(concurrent sweep)
注意初始标记和重新标记还是会stop the world,但是在耗费时间更长的并发标记和并发清除两个阶段都可以和用户进程同时工作。
CMS如何解决内存碎片化的问题
***可以让CMS在进行一定次数的Full GC(标记清除)的时候进行一次标记整理算法,CMS提供了以下参数来控制:
-XX:UseCMSCompactAtFullCollection -XX:CMSFullGCBeforeCompaction=5
也就是CMS在进行5次Full GC(标记清除)之后进行一次标记整理算法,从而可以控制老年带的碎片在一定的数量以内,甚至可以配置CMS在每次Full GC的时候都进行内存的整理。***
4.G1收集器
![](https://img.haomeiwen.com/i14404345/282cf24b2151e526.png)
五.GC面试题
1.Object的finalize()
![](https://img.haomeiwen.com/i14404345/2b973d86703cc145.png)
2.java当中的强引用,软引用,弱引用,虚引用
![](https://img.haomeiwen.com/i14404345/bcae2e2be8b967b8.png)
![](https://img.haomeiwen.com/i14404345/22ef2556dd49155e.png)
虚引用:方便我们了解垃圾回收是什么是怎么进行的。
3.减少GC的次数
只要执行GC,就会触发stop-the-world,暂停程序,造成卡顿,GC的优化就是减少stop-the-world的时间。
1.对象不用时最好显示置为 NULL
一般而言,为 NULL 的对象都会被作为垃圾处理,所以将不用的对象置NULL,有利于 GC 收集器判定垃圾,从而提高了 GC 的效率。
2.尽量少使用 System,gc()
此函数建议 JVM 进行主 GC,会增加主 GC 的频率,增加了间接性停顿的次 数。
3.尽量少使用静态变量
静态变量属于全局变量,不会被 GC 回收,他们会一直占用内存
4.尽量使用 StringBuffer,而不使用 String 来累加字符串
5.分散对象创建或删除的时间
集中在短时间内大量创建新对象,特别是大对象,会导致突然需要大量内存, JVM 在这种
情况下只能进行主 GC 以回收内存,从而增加主 GC 的频率。
6.尽量少用 finaliza 函数
它会加大 GC 的工作量。
7.如果有需要使用经常用到的图片,可以使用软引用类型,将图片保存在内存中,而不引起 outofmemory
8.能用基本类型入 INT 就不用对象 Integer 9.增大-Xmx 的值
网友评论