大话JVM(一):垃圾收集算法

   系列介绍|本系列主要是记录学习jvm过程中觉得重要的内容,方便以后复习

在说垃圾收集算法之前,先要说一下垃圾收集,从大的讲,垃圾收集需要考虑三件事情:

  1.哪些内存需要回收

  2.什么时候回收

  3.如何回收

JVM在执行java程序时,把他管理的内存分为多个数据区域:

1.程序计数器(记录程序执行到哪了,各线程之间独立存储, 互不影响)

2.虚拟机栈(这个栈就是我们常说的jvm的“堆”和“栈”中的栈,这里存放的是编译期间可知的各种数据类型(8种基本类型)、对象引用(reference类型,就是一种数据指针,指向对象的起始地址,或者句柄,或者是对象相关的位置)

3.本地方法栈(这个跟虚拟机栈非常相似,只不过虚拟机栈是为虚拟机执行java方法服务的,而本地方法栈是为虚拟机使用Natvie方法服务的,虚拟机规范中没有对本地方法栈做强制规定,HotSpot把虚拟机栈和本地方法栈合二为一了)

4.java堆(这堆是JVM管理内存中最多的一块,几乎所有的对象实例都存放在这里,java虚拟机规范中描述:所有对象的实例以及数据都要在堆上分配,GC就是主要管理这个区域)

5.方法区(在HotSpot中,这个区就是我们常说的“永久代”,这是一个线程共享的区域,它主要用来存储被虚拟机加载的类信息、常量、静态变量、JIT编译后的代码等数据)

其中程序计数器、虚拟机栈、本地方法栈3个区随线程而生,随线程而灭;栈中的栈帧随方法的进入和退出有条不紊的执行出栈和入栈操作,内存的分配是在类结构确定下来时就已知的,内存的分配和回收都具有确定性,因此这几个区域不需要过多考虑回收问题,因为方法结束或线程结束时,内存自然跟着回收了。主要考虑的是JAVA堆和方法区,因为这部分内存分配是动态的,程序在运行时才知道创建哪个对象实例,执行哪个方法。

GC回收前需要考虑对象已经“死了吗

判断对象是否存活有两种算法,一种是引用计数算法,另一种是可达性算法

1)引用计数算法

引用计数算法就是给对象中添加一个引用计数器,每当有地方引用他时,计算器值加1,当引用失效时,计数器值减1,计算器值为0时,表示对象不再被使用。

引用计数算法实现简单,判定效率高,但是有个致命确定,就是循环引用时无法正常工作。

 1 public class CountGC {
 2
 3     public Object instance = null;
 4
 5     public static void testGC(String[] args){
 6         //创建了一个CountGC对象,并发把它赋给了countGC1,CountGC的对象引用计数值加1
 7         CountGC countGC1 = new CountGC();
 8         //又创建了一个CountGC对象,并发把它赋给了countGC2,另一个CountGC的对象引用计数值加1
 9         CountGC countGC2 = new CountGC();
10
11         //把第一个CountGC对象的instance字段赋值上第二个CountGC对象,第二个CountGC对象引用计数值再加1,这是就变成了2
12         countGC1.instance = countGC2;
13         //把第二个CountGC对象的instance字段赋值上第一个CountGC对象,第一个CountGC对象的引用计数值再加2,这时也变成了2
14         countGC2.instance = countGC1;
15
16         //countGC1赋空值,第一个CountGC对象引用减1
17         countGC1 = null;
18         //countGC2赋空值,第二个CountGC对象引用减1
19         countGC2 = null;
20
21
22         //如果这时候回收,这两个CountGC对象是无法回收的,因为他们的引用计数值不为0
23         System.gc();
24     }
25 }

2)可达性算法

可达性算法就是以一个 GC Roots对象向下搜索,能搜索到的对象就说明是存活的,搜索不到的对象说明就是不可用的。

不管是引用计数算法,还是不可达算法,最终判断对象是否存活的关键,是引用。

下面我们正式介绍垃圾收集算法,我们主要介绍下面几种算法:

1.标记 - 清除算法

2.复制算法

3.标记 - 整理算法

4.分代收集算法

1)标记 - 清除算法

标记 - 清除算法就跟他的名字一样,分为“标记”和“清除”两阶段,首选标记出所有可回收的对象,然后统一回收所有被标记的对象。

标记 - 清除算法是最基础的算法,后续的几个算法都是基于这种算法思路对其不足进行改进得到的。

它的不足主要表现在两个方面,一是效率问题,二是空间利用问题

效率不高是因为,它标记是需要遍历所有内存空间,清除时也是一个个清除

空间利用率问题是因为清理后内存空间是零碎的,当需要分配大空间时,没有连续空间,需要再次触发GC

回收前状态:

回收后状态:

             

2)复制算法

为了解决效率问题,复制算法出现了,它将可用内存按容量划分为大小相等的两块,每次只使用其中一块,当这一块的内存用完时,就将存活的对象复制到另外一块内存上面,然后把使用过的内存空间一次清理掉。

回收前状态:

回收后状态:

                

复制算法的优点是:实现简单,运行高效

缺点是:浪费内存,从上面算法来看,实际使用只有原来内存的一半,浪费太大了。

现在的商用虚拟机都采用这种算法来回收新生代,IBM公司专门研究表明,新生代中的对象98%都是“朝生夕死”的,所以并不需要按照1:1来划分内存,而是将内存分成一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和一块Survivor,另一个Survivor作为预留空间。(HotSopt中默认分配比例是8:1:1,这样只浪费了10%的空间)。

采用这种内存分配方式的回收过程:

1.每次使用Eden和一块Survivor,另一块Survivor作为预留空间,

2.标记出Eden和一块Survivor空间中存活的对象,并复制到另一块Survivor空间中

3.清理掉使用过的Eden和一块Survivor空间。

这种做法有一个不足之处,就是当Eden和一块Survivor空间中存活的对象(对象存活率较高)大于另一块Survivor空间时,需要老年代担保分配,这时候效率非常低(因为存活对象是一个个复制到预留内存空间的,对象存活率特别高时,对象数也会非常多),而且还浪费空间(这时候预留空间就起不到作用了,浪费了预留空间的内存)。

因为当发现Eden和一块Survivor空间中存活的对象大于另一块Survivor空间时,这时候会把存活对象直接分配到老年代。

3)标记 - 整理算法

标记 - 整理算法就是把标记 - 清除算法中的清除替换成整理,整理时不直接对回收对象清理,而是让所有存活对象向一端移动,然后直接清理掉边界以外的内存。

回收前状态:

回收后状态:

            

这个算法是根据老年代的特点设计出来的,因为老年代中对象存活率较高,并且没有额外的空间对它进行分配担保,就必须采用“标记 - 清楚” 或者 “标记 - 整理”算法来进行回收。

4)分代收集

当前商业虚拟机都是采用“分代收集”算法,这种算法并没有什么新的思想,只是按照对象存活周期不同将内存划分为几个区域,就像java堆中的新生代和老年代一样,这样做的好处是,可以根据各个年代的特点采用适当的收集算法。

原文地址:https://www.cnblogs.com/jpwahaha/p/10639662.html

时间: 2024-10-22 17:09:18

大话JVM(一):垃圾收集算法的相关文章

JVM(五)-垃圾收集算法和垃圾收集器

一.垃圾收集算法 (1)标记-清除算法 "标记-清除"(Mark-Sweep)算法,如它的名字一样,算法分为"标记"和"清除"两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收掉所有被标记的对象.之所以说它是最基础的收集算法,是因为后续的收集算法都是基于这种思路并对其缺点进行改进而得到的. 它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高:另外一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致,

JVM中垃圾收集算法总结

??通过前面的介绍我们了解了对象创建和销毁的过程.那么JVM中垃圾收集器具体对对象回收采用的是什么算法呢?本文主要记录下JVM中垃圾收集的几种算法. JVM的垃圾回收的算法 标记-清除算法(Mark-Sweep) ??标记清除算法是最基础的回收算法,该算法分为两个阶段,即标记阶段和清除阶段. |阶段|说明 | |--|:--| | 标记阶段|先根据可达性分析算法找出需要回收的对象进行标记 | | 清除阶段| 统一回收被标记的对象| 参考<深入理解java虚拟机>: 从可达性分析算法角度看标记-

深入浅出JVM之垃圾收集算法

判断哪些对象需要被回收 引用计数算法: 给对象中添加一个引用计数器,每当有一个地方引用时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用的. 但是JVM没有使用此方法,因为此方法无法解决2个对象相互循环引用的问题. 可达性分析算法: 这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象

【Java 之 JVM】垃圾收集算法

一.判断对象是否存活--可达性分析算法 基本思想:通过一系列成为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots 没有任何引用相连则证明该对象是不可用的.在判断完无链之后,还需经历两次标记过程,才会真正回收,暂略. 二.垃圾收集算法 1.标记--清除算法 标记出所有需要回收的对象,标记完统一回收.缺点:标记,清除2个过程效率都不高,大量不连续的内存碎片. 2.复制算法 将内存划分为大

JVM虚拟机——垃圾收集算法

原文博客链接:http://www.cnblogs.com/leesf456/p/5218594.html 1.哪些内存需要进行垃圾回收 对于虚拟机中线程私有的区域,如程序计数器.虚拟机栈.本地方法栈都不需要进行垃圾回收,因为它们是自动进行的,随着线程的消亡而消亡,不需要我们去回收,比如栈的栈帧结构,当进入一个方法时,就会产生一个栈帧,栈帧大小也可以借助类信息确定,然后栈帧入栈,执行方法体,退出方法时,栈帧出栈,于是其所占据的内存空间也就被自动回收了.而对于虚拟机中线程共享的区域,则需要进行垃圾

深入理解JVM(二)--垃圾收集算法

一. 概述 说起垃圾收集(Garbage Collection, GC), 大部分人都把这项技术当做Java语言的伴随生产物. 事实上, GC的历史远远比Java久远, 1960年 诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言. 当Lisp还在胚胎时期时,人们就在思考GC需要完成的三件事情: 哪些内存需要回收? 什么时候回收? 如何回收? 现在内存的动态分配与内存回收技术已经相当成熟, 那为什么我们还要去了解GC和内存分配呢? 答案很简单: 当需要排查各种内存溢出, 内

java虚拟机(jvm)垃圾收集算法剖析

目前商业虚拟机的垃圾收集都采用"分代收集"算法,这种算法根据对象的存活周期把内存划分为"新生代"和"老年代".然后根据不同的年 代的特点采取不同的收集算法.在新生代中,由于每次minor GC都发现有很多对象死去,存活的非常少,因此采用复制算法.而老年代中对 象存活率非常的高,而起没有额外的内存空间进行分配担保就采用"标记-清理"或"标记-整理"算法来进行回收.但是基本上使用的"标记-整理&quo

[读书笔记]JVM的垃圾收集算法

1.标记-清除算法 (1)算法思想:第一步,标记好所有需要回收的对象:第二步,清除被标记的对象. (2)缺点:时间上,效率不高,无论是标记还是清除操作:空间上,会产生大量不连续的内存碎片,使得占用大内存的对象找不到足够的连续内存而进一步又提前触发一次GC. 标记-清除算法执行过程如下图所示: 2.复制算法 (1)算法思想:内存区分成两部分大小相等的区域.一半作为使用区,一半作为保留区.申请内存时,在使用区进行.GC时,我们将使用区的将被存活的对象复制到保留区,然后将使用区清空,这时我们将原使用区

《深入理解JAVA虚拟机》JDK的垃圾收集算法

概念 垃圾收集是很多使用JAVA语言的IT从业者了解得比较少的地方. 但是涉及性能时非常重要.大公司面试除了算法,这部分也是会经常考察的地方. <深入理解JAVA虚拟机>一书中讲到JVM的垃圾收集算法和垃圾收集器. 垃圾收集算法分为: 1.标记清除算法 通常用在回收老年代内存. 最早的搜集算法就是标记清除(Mark-Sweep)算法了. 其原理是分为标记和清除两个阶段: 首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象. 主要不足两个: 一个是效率问题,标记过程和清除过程效率