借鉴【JAVA核心】Java GC机制详解老莫的博客-CSDN博客javagc
[Java面试--垃圾回收机制(GC)小刺猬喜歡獨角獸-CSDN博客java垃圾回收机制面试题
CMS垃圾收集器云袭的专栏-CSDN博客cmsparallelremarkenabled
-
==这篇很乱,期末周,工科背市场营销已经傻了,我不知道在写啥,主要可以看上面两篇,可以以第一篇为主,第二篇为辅第二篇的算法和详细==
一. 什么是GC
如何及时的把不再使用的对象清除将内存释放出来,这就是GC要做的事。JVM在进行GC时,并不是对这三个区域统一回收。 大部分时候,回收都是新生代~
GC的作用区
垃圾回收区域(主要针对无用堆对象实例回收和常量池的回收和类型的卸载)
存储虚拟机加!载的类信息常量静态变量即时编译器编译后的代码运行时常量池等
GC的对象
需要进行回收的对象就是已经没有存活的对象,判断一个对象是否存活常用的有两种办法:引用计数和可达分析。
(1)引用计数:
每个对象有一个引用计数属性,新增一个引用时计数加1,引用释放时计数减1,计数为0时可以回收。此方法简单,无法解决对象相互循环引用的问题。 引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1。 优点:实现简单,判断效率高 缺点:很难解决对象之间的相互循环引用,所以java语言并没有选用引用计数法管理内存
public class ReferenceFindTest {
public static void main(String[] args) {
MyObject object1 = new MyObject();
MyObject object2 = new MyObject();
object1.object = object2;
object2.object = object1;
object1 = null;
object2 = null;
}
这段代码是用来验证引用计数算法不能检测出循环引用。最后面两句将object1和object2赋值为null,也就是说object1和object2指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为0,那么垃圾收集器就永远不会回收它们。
(2)可达性分析(Reachability Analysis):
从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。不可达对象。 可达性分析算法是从离散数学中的图论引入的,程序把所有的引用关系看作一张图,从一个节点GC ROOT开始,寻找对应的引用节点,找到这个节点以后,继续寻找这个节点的引用节点,当所有的引用节点寻找完毕之后,剩余的节点则被认为是没有被引用到的节点,即无用的节点,无用的节点将会被判定为是可回收的对象。
在Java语言中,可作为GC Roots的对象包括下面几种: a) 虚拟机栈中引用的对象(栈帧中的本地变量表); b) 方法区中类静态属性引用的对象; c) 方法区中常量引用的对象; d) 本地方法栈中JNI(Native方法)引用的对象。
什么时候触发GC
(1)程序调用System.gc时可以触发
(2)系统自身来决定GC触发的时机(根据Eden区和From Space区的内存大小来决定。当内存大小不足时,则会启动GC线程并停止应用线程)
GC又分为 Minor GC 和 Full GC (也称为 Major GC )
-
Minor GC 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Minor GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
-
Full GC 对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC: a) 年老代(Tenured)被写满; b) 持久代(Perm)被写满; c) System.gc()被显示调用; d) 上一次GC之后Heap的各域分配策略动态变化;
GC的工作
JVM在进行GC时,并不是对这三个区域统一回收。 大部分时候,回收都是新生代~
二 . GC常用算法
标记-清除算法,标记-压缩算法,复制算法,分代收集算法。
标记—清除算法
-
思想:在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。 不会产生内存碎片,但是依旧移动对象的成本。
-
优点 最大的优点是,标记—清除算法中每个活着的对象的引用只需要找到一个即可,找到一个就可以判断它为活的。此外,更重要的是,这个算法并不移动对象的位置。
-
缺点 它的缺点就是效率比较低(递归与全堆对象遍历)。每个活着的对象都要在标记阶段遍历一遍;所有对象都要在清除阶段扫描一遍,因此算法复杂度较高。没有移动对象,导致可能出现很多碎片空间无法利用的情况。
标记-压缩算法(标记-整理)
-
标记-清除算法采用从根集合(GC Roots)进行扫描,该算法并没有直接对死亡的对象进行清理,而是将所有存活的对象整理一下,放到另一处空间,然后把剩下的所有对象全部清除。这样就达到了标记-整理的目的。主要用于虚拟机
-
优点该算法不会像标记-清除算法那样产生大量的碎片空间。
-
缺点如果存活的对象过多,整理阶段将会执行较多复制操作,导致算法效率降低。
复制算法
-
复制算法将内存平均分成两部分,然后每次只使用其中的一部分,当这部分内存满的时候,将内存中所有存活的对象复制到另一个内存中,然后将之前的内存清空,只使用这部分内存,循环下去。
-
缺点复制的代价较高,所以适合新生代,因为新生代的对象存活率较低,需要复制的对象较少; 需要双倍的内存空间,而且总是有一块内存空闲,浪费空间
-
优点不产生碎片、易操作
分代收集算法
from - to 谁是0谁是to 当两个都是1是,随机一个变成2 另一个是0做to(这个狂神讲的也很细)
垃圾收集器
如果说收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现
Serial收集器(复制算法)
串行收集器是最古老,最稳定以及效率高的收集器,可能会产生较长的停顿,只使用一个线程去回收
-XX:+UseSerialGC来强制执行
-
新生代、老年代使用串行回收
-
新生代复制算法
-
老年代标记-压缩
并行收集器
ParNew(停止-复制算法)
新生代收集器,可以认为是Serial收集器的多线程版本,在多核CPU环境下有着比Serial更好的表现
-XX:+UseParNewGC(new代表新生代,所以适用于新生代)
-
新生代并行
-
老年代串行
Parallel收集器
类似ParNew 新生代复制算法 老年代标记-压缩 更加关注吞吐量 -XX:+UseParallelGC
-
使用Parallel收集器+ 老年代串行
-XX:+UseParallelOldGC
-
使用Parallel收集器+ 老年代并行
CMS收集器
CMS收集器是为了低延迟而生,通过尽可能的并行执行垃圾回收的几个阶段来把延迟控制到最低。CMS收集器是老年代的垃圾收集器,一般情况下会有ParNew来配合执行(默认情况下也是ParNew),ParNew也是使用并行的算法来执行年轻代的回收。当然除此之外,你还可以选择使用Serial收集器来收集年轻代,不过一般很少这样使用。通过咱们说的CMS收集器是指广义上的CMS收集器,包含以下几个:ParNew(Young)GC + CMS(Old)GC + Serial GC 算法(应对核心的CMS GC某些时候的不赶趟,开销很大)。本文重点介绍一些CMS在Old区域的回收。 CMS垃圾收集器云袭的专栏-CSDN博客cmsparallelremarkenabled
G1收集器
CMS收集器与G1收集器区别_xiaoyi5224765的博客-CSDN博客
finalize()方法详解
没看懂,而且有点老了,等用了再说吧