jvm垃圾收集算法和收集时机

哪些内存需要回收

什么时候回收

如何回收

垃圾回收线程是守护线程,平常到达安全点和安全区域时会回收,当堆内存占用到达上限时Full GC

3.1引用计数算法和可达性分析算法

3.1.1引用计数算法

在对象中添加一个引用计数器,每当有一个引用它时计数器就加一;当引用失效时计数器就减一;任何时刻计数器为零的对象就是不可能再被使用的;

缺点:如果两个对象相互引用不会被回收

3.1.2可达性分析算法

一般虚拟机采用的是这种方法

基本思路:通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始根据引用关系向下搜索,搜索过程走过的路径称为“引用链”,如果某个对象到GC Root之间没有任何引用链,代表对象不可达,则证明此对象不会再被使用

GC Root 有(可以根据具体的场景指定,下边的相当于是全局回收时用到的):

虚拟机栈 中引用的对象

方法区 中类静态属性引用的对象,常量引用的对象

虚拟机内部的引用  Class对象中的一些异常对象 空指针,堆溢出

同步锁持有的对象 synchronized

当分代回收时,是局部回收,就可以指定要回收的代为GC Root

强引用、软引用、弱引用、虚引用

为了再次细化垃圾回收的时机,又将已经被引用的对象细分为这四种状态

强引用:一般理解的引用 Object object = new Object();

软引用:还有些用但非必须的对象,GC之后内存还是不够,会在内存溢出之前对这些对象进行二次回收

弱引用:表示只能生存到下次垃圾回收

虚引用:虚引用不会影响回收时间,只是会在回收时收到一个系统通知

3.2方法区的回收

方法区的回收性价比比较低,是否回收可以通过配置设置

回收条件:

1.类所有的实例已经被回收,包括所有的派生类

2.这个类的类加载器已经被回收,这种场景存在于OSGI JSP的重新加载

3.类的Class对象没有任何地方被引用

3.3垃圾回收算法

准备工作:分代,堆分为年轻代和老年代(G1是将堆分为若干Region)

针对不同的分代采用不同的回收类型

Minor GC(年轻代)  Major GC(老年代) Full GC(堆+方法区)

不同的区域也可以采用不同的算法:标记-复制、标记-清除、标记-整理

3.3.1标记清除算法

标记要清除的对象或者标记不需要清除的对象,然后进行清除

缺点:

1.如果需要回收的对象过程,需要进行大量的标记清除动作,执行效率会降低

2.容易产生内存碎片,内存利用率低

“分区空闲链表” 表明现在的可用空间

3.3.2标记复制算法

基本思想“半区复制”,将内存安装大小分为两个相同的区域,每次只使用其中的一块,当这块用完时,将活着的对象复制到另一块,再将原来的一块清空。

实际使用中不按1:1的比例划分,好比是9:1的比例,复制时第二块就有可能装不下就需要有其他内存做为担保,一般用老年代做担保。

老年代没有担保空间,就不方便采用这种算法

HotSpot对年轻代采用的是这种算法,将年轻代分为Eden和两个Survivor

3.3.3标记整理

标记对象,将存活的对象移动至内存前端

缺点:对象移动时需要用户程序停止即“Stop The World”

可以将标记清除和标记整理配合使用,当内存碎片影响内存分配时使用一次标记整理

3.4OopMap、安全点、安全区域

一旦触发了垃圾回收,如何确定垃圾回收线程的执行时间

Hotspot为了确定垃圾回收的时间引入了安全点和安全区域的概念,安全点和安全区域代表内存区域没有发生变化,这个时候能够允许回收线程和用户线程可以同时进行

3.4.1用OopMap埋点  这个重新看一下,寻找对象的引用关系应该是内存的角度

Hotspot在类加载完成时用OoMap的数据结构记录了对象的引用关系,在编译完的字节码文件中有这种记录的体现,当进行GC时不需要从GC Roots开始进行查找

OopMap在字节码文件表明从某个位置到某个位置引用了某个对象

3.4.2安全点

OopMap记录的位置即为安全点

用户线程到达安全点之后会将自己挂起,等待GC线程执行

安全点:方法调用、循环跳转、异常跳转

3.4.3安全区域

某一段代码片段之中,引用关系不会发生变化

用户线程进入安全区域之后会意识到自己进入了安全区域,当要出安全区域时先判断GC操作是否完成如果没有就会等待直到完成

你可能感兴趣的:(jvm垃圾收集算法和收集时机)