概述
浅谈GC
1、什么是GC
我们都知道,Java不同于C,C++的一点就是Java不需要手动回收垃圾资源,原因就是Java有GC
所以,什么是GC呢?
GC(Garbage Collection)就是垃圾收集的意思,我们在使用Java的时候不需要专门的编写内存回收和垃圾清理的代码,GC机制会自动帮你实现
2、怎么判断一个对象是否死亡
既然GC会自动帮我们清理垃圾,那么GC怎么知道哪些代码是要被清理的呢?
这点不要担心,GC在进行清理之前,首先会判断哪些对象已经死亡了。那么它是怎么判断的呢?
有两种方式:
(1)引用计数法
每个对象在创建的时候给它添加一个引用计数器,每当有一个地方引用它时,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
但是这个方法很难解决对象之间的相互循环引用的问题
(2)可达性分析算法
这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
这里要注意:不可达的对象并非“非死不可”
对象被真正宣告死亡需要两次标记,首先在可达性分析中不可达的对象被第一次标记,然后判断给对象是否需要执行finalize方法,当对象没有覆盖或者已经被调用过这个方法时,那么就不标记,否则就执行finalize方法,进行第二次标记。然后被回收
什么是finalize方法
当对象不再被任何对象引用时,GC会调用该对象的finalize()方法
finalize()是Object的方法,子类可以覆盖这个方法来做一些系统资源的释放或者数据的清理
可以在finalize()让这个对象再次被引用,避免被GC回收;但是最常用的目的还是做cleanup
Java不保证这个finalize()一定被执行;但是保证调用finalize的线程没有持有任何user-visible同步锁。
在finalize里面抛出的异常会被忽略,同时方法终止。
当finalize被调用之后,JVM会再一次检测这个对象是否能被存活的线程访问得到,如果不是,则清除该对象。也就是finalize只能被调用一次;也就是说,覆盖了finalize方法的对象需要经过两个GC周期才能被清除。
3、什么是引用
上面一直在说引用,那什么是引用呢?
在JDK1.2以后,Java将引用分为强引用、软引用、弱引用、虚引用四种(引用强度逐渐减弱)
1.强引用
以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
2.软引用(SoftReference)
如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。
3.弱引用(WeakReference)
如果一个对象只具有弱引用,那就类似于可有可无的生活用品。弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中。
4.虚引用(PhantomReference)
"虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
虚引用主要用来跟踪对象被垃圾回收的活动。
虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
4、如何判断一个常量是废弃常量
没有对象引用该常量的时候,就说明是废弃常量了
5、如何判断一个类是无用的类
同时满足下面3个条件才能算无用的类:
(1)该类所有实例都被回收了,也就是说Java堆中不存在该类的任何实例
(2)加载各类的ClassLoader已经被回收了
(3)该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
6、浅谈各种垃圾收集算法
主要采用下面四种收集算法:标记-清除算法、复制算法、标记-整理算法、分代收集算法
(1)标记-清除算法
最直接的方法:将内存中要清除的对象标记出来,然后所有标记结束之后就统一回收被标记的对象
(2)复制算法
将内存分为大小相同的两块,每次使用其中一块。当这一块使用完之后,将就还存活的对象复制到另一块去,然后在把使用的空间清理。
(3)标记-整理算法
和第一个一样先标记所有要清理的对象,然后让所有存活的对象向一端移动,然后直接清理另一端的内存
(4)分代收集算法(重点)
首先讲一下分代收集算法的堆空间基本结构:
对象被创建之后在eden区。s0,s1是新生代区,tentired是老年代区
大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s1(“To”),并且对象的年龄还会加 1(Eden 区->Survivor 区后对象的初始年龄变为 1),当它的年龄增加到一定程度(默认为 15 岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 -XX:MaxTenuringThreshold
来设置。经过这次GC后,Eden区和"From"区已经被清空。这个时候,“From"和"To"会交换他们的角色,也就是新的"To"就是上次GC前的“From”,新的"From"就是上次GC前的"To”。不管怎样,都会保证名为To的Survivor区域是空的。
在新生代区,我们采用复制短发,因为新生代区每次收集都会有大量对象死去,使用复制算法更快
在老年代区,我们采用标记-清除或标记-整理算法,因为老年代对象存活几率高
7、垃圾收集算法的具体实现
常用的垃圾收集器:Serial收集器、ParNew收集器、Parallel Scavenge收集器、CMS收集器、G1收集器
参考了一个大神的总结:https://snailclimb.top/JavaGuide/#/java/jvm/JVM%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6
这个大神写了好多Java有关的知识,大家可以都去看一下,受益颇多
最后
以上就是可爱豌豆为你收集整理的浅谈GC的全部内容,希望文章能够帮你解决浅谈GC所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复