JVM垃圾回收

24 篇文章 0 订阅

垃圾回收

概述

垃圾收集机制是Java的招牌能力,极大的提高了效率,部分语言没有垃圾收集技术,需要手动进行收集。垃圾收集不是Java语言的伴生产物,第一个使用内存动态分配和垃圾收集计数的是Lisp语言

什么是垃圾?

垃圾是指在运行程序中没有任何引用指向的对象,如果不及时对内存中的垃圾进行清理,那么这些垃圾对象所占用的内存空间会一直保留到应用程序结束,被保留的空间无法被其他对象使用,甚至可能导致内存溢出

GC(垃圾回收)

为什么需要GC

1.如果不及时清理这些垃圾对象,会导致内存溢出。

2.在回收时,还可以将内存碎片进行整理(数组必须是连续空间)。

早期垃圾回收

在C/C++时代,开发人员需要new关键字进行内存申请,并使用delete关键字进行内存释放,这种方式可以灵活控制内存释放的时间。倘若有溢出内存区间忘记回收,就会产生内存泄漏

内存溢出和内存泄漏

内存溢出:经过垃圾回收后,内存中任然无法存储新创建的对象,内存不够用而导致溢出

内存泄漏:生命周期很长的对象,一些已经用不到的对象,但是垃圾回收器不能判定为垃圾,这些对象就一直占用着内存,成为内存泄漏。大量的此类对象存在,也是导致内存溢出的原因

Java垃圾回收机制

自动内存管理

优点:解放程序员,对内存管理更合理,自动化

缺点:对程序员的内存管理能力降低了,解决问题的能力变弱了,不能自主调整垃圾回收的机制

垃圾回收的主要阵地

垃圾回收可以对年轻化hi收,也可以对老年代回收,甚至是全栈和方法区二点回收,其中Java堆是垃圾回收器的工作重点。

从此上讲:频繁收集Young区,较少收集Old区,基本不收集元空间(方法区)

垃圾回收相关算法

垃圾标记阶段算法

垃圾标记阶段:主要是为了判断对象是否是垃圾对象,是否有引用指向对象

相关的标记算法:引用计数算法  和   可达性分析算法

引用计数算法(在现代的jvm中并没有被使用)

有个计数器来记录对象的引用数量

String s1 = new String("aaa");
String s2 = s1;//有两个引用变量指向aaa对象
s2=null;//引用变量-1
s1=null;//引用变量-1

缺点:需要维护计数器,占用空间,频繁操作,需要时间开销;

        无法解决循环引用问题(多个对象之间相互引用,没有其他外部引用指向他们,计数器都不为0,不能回收,产生内存泄漏)。

可达性分析算法/根搜索算法

实现思路:从一些根对象(GCRoots)的对象出发查找,与根对象直接或间接连接的对象就是存活对象,不与根对象引用链连接的对象就是垃圾对象

GCRoots是那些元素

1.虚拟机栈中引用的对象

2.方法区中类静态属性引用的对象

3.所有被同步锁  synchronized  持有的对象

4.Java虚拟机内部的引用:         基本数据类型对应的Class对象,一些常驻的异常对象(NullPointerException   OutofMemoryError),系统类加载器

对象的finalization机制

当一个对象被标记为垃圾后,在真正被回收之前,会调用一次Object类中的finalize(),是否还有逻辑需要进行处理

有了finalization机制的存在,在虚拟机中把对象状态分为3种:

                  1.可触及的  不是垃圾,与根对象连接的

                  2.可复活的   判定为垃圾了,但是还没有调用finalize(),(在finalize()中对象可能会复活)

                  3.不可触及的:判定为垃圾了,finalize()也已经执行过了,这种就是必须被回收的对象

垃圾回收阶段的算法

        常见的三种垃圾收集算法:  标记-复制算法(Copying)    标记-清除算法(Maek-Sweep)   标记-压缩算法(Mark-Compact)

标记-复制算法

        将内存分为大小相等的两份空间,把当前使用的空间种存活的对象 复制到另一个空间种,将正在使用的空间中垃圾对象清除

        优点:减少内存碎片

        缺点:如果需要复制的对象数量多,效率低

标记-清除算法

        清除不是真正的把垃圾对象清除掉,将垃圾对象地址维护到一个空闲列表中,后面有新对象到来时,覆盖掉垃圾对象即可

        特点:实现简单、效率低、回收后有碎片产生

标记-压缩算法(标记-整理算法)

        第一阶段,送根节点开始标记所有被引用对象

        第二阶段将所有的存活对象压缩到内存的一端,按顺序排放,之后清除边界外所有的空间

       优点:消除了标记-清除算法当中,内存区域分散的缺点,我们需要给新对象分配内存时,JVM只需要持有内存的起始地址即可。消除了复制算法当中,内存减半的高额代价

        缺点:从效率上来说,标记-压缩算法要低于复制算法。移动对象的同时,如果对象被其他对象引用,则还需要调整引用的地址。一定过程中,需要全部暂停用户应用程序。即:STW((Stop the World)指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生式整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉)

垃圾回收算法小结

标记清除标记整理复制
速率中等最慢最快
空间开销少(但会堆积碎片)少(不堆积碎片)通常需要活对象的2倍空间(不堆积碎片)
移动对象

分代收集

        为什么要使用分代收集?

                不同的对象的生命周期时不一样的。因此,不同的生命周期的对象可以采取不同的收集方式,以便提高回收效率。

        在HotSpot中,基于分代的概念,GC锁使用的内存回收算法必须结合年轻代和老年代各自的特点。

        年轻代:区域相对老年代较小,对象生命周期短,存活率低,回收频繁

        这种情况复制算法的回收整理,速度时最快的。复制算法的效率之和当前存活对象大小有关,因此很适用于年轻代的回收。而复制算法内存利用率不高的问题,通过hotspot中的两个survivior的设计得到缓解

        老年代:区域较大,对象生命周期长,存活率高,回收不及年轻代频繁

        这种情况存在大量存活率高的对象,复制算法明显变得不适合。一般是由标记-清除或者是标记-清除与标记-压缩的混合实现

        1.Mark阶段的开销与存活对象的数量成正比

        2.Sweep阶段的开销与锁管理区域的大小成正比

        3.Compact阶段的开销与存活对象的数据成正比、

分代的思想被现有的虚拟机广泛使用,几乎所有的垃圾回收器都区分新生代和老年代

垃圾回收器

概述

        垃圾收集器是垃圾回收的实际实现者,垃圾回收算法是方法论

垃圾回收器分类

       按照线程数量分为

                单线程垃圾回收器:Seria ,Seria old

                多线程垃圾回收器:Parallel

        按照工作模式分为

                独占式:垃圾回收线程执行时,其他线程暂停

                并行式:垃圾回收线程可以和用户线程同时执行

        按工作的内存区间分为

                年轻代垃圾回收器

                老年代垃圾回收器

        垃圾回收器性能指标

                暂停时间

                吞吐量   

                回收速度       

                占用内存大小

CMS垃圾回收器

        Concurrent Mark Sweep 并发标记清除

        垃圾回收过程

                支持垃圾回收线程与用户线程并发(同时)执行

                初始标记:独占式的暂停用户进程

                并发标记:垃圾回收线程与用户线程并发(同时)执行

                重新标记:独占式的暂停用户线程

                并发清除:垃圾回收线程与用户线程并发(同时)执行 进行垃圾对象清除

        优点:可以做到并发收集

        缺点:使用标记清除算法,会产生内存碎片,并发执行影响到用户线程,无法处理浮动垃圾

 三色标记算法

                由于cms由并发执行过程,所以在标记垃圾对象时有不确定性,所以在标记是,将对象分为3种颜色(3种状态)。

                        黑色:例如GCRoots确定是存活的对象

                        灰色:在黑色对象中关联的对象,其中还有未扫描完的,之后还需要再次进行扫描

                        白色:与黑色,灰色对象无关联的,垃圾收集算法不可达的对象

        标记过程

                1.先确定GCRoots,把GCroots标记为黑色

                2.与GCRoots关联的对象标记为灰色

                3.再次遍历灰色,灰色变为黑色,关联的对象变为灰色

                4.最终保留黑色,灰色,回收白色对象

        注:这个过程正确执行的前提是没有其他线程改变对象见的引用关系,然而,并发标记的过程中,用户线程仍在运行,因此就会产生漏标和错标的情况

        漏标

                                                

       假设GC已经在遍历对象B了,而此时用户线程执行了A.B=null的操作,切断了A到B的引用,本来执行A.B=null之后,B、D、E都可以被回收了,但是由于B已经变为灰色,它仍会被当作存活对象,继续遍历下去。最终的结果就是本轮GC不会回收B、D、E,留到下次GC时回收,也算浮动垃圾的一部分。

        错标

                                               

                 假设GC线程已经遍历到B了,此时用户线程执行了一下操作:

                        B.D=null;//B到D的引用被切断

                        A.xx=D;//从A到D的引用被建立

                B到D的引用被切断,且A到D的引用被建。此时GC线程继续工作,由于B不再引用D了,尽管A又引用了D,但是因为A已经标记为黑色,GC不会再遍历A了,所以D会被标记为白色,最后被当作垃圾回收。

   解决错标的问题

        错标产生的原因

                只有满足以下两种情况才会发生错标

                                                灰色指向白色的引用全部断开

                                                黑色指向白色的引用被建立        

        原始快照和增量更新

                原始快照:当灰色对象指向白色对象的引用被断开时,就将这条引用关系记录下来。当扫描结束后,再一这些灰色对象韦根,重新扫描一次。

                增量更新:当黑色指向白色的引用被建立时,就将这个新的引用关系记录下来,等扫描结束后,再以这些记录中的黑色对象为根,重新扫描一次。相当于黑色对象一旦建立了指向白色对象的引用,就会变为灰色对象

G1(Garbage First)回收器

        将堆内存各个区有分文较小的多个区域,对这些个区域进行监测,对某一个区域中垃圾数量大的区域优先回收。
            也是并发收集的。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值