java判断垃圾已回收_JAVA语言的垃圾判断回收算法

一、怎样判断是否是垃圾(垃圾判断算法)?

JAVA语言的显著特点是引入了垃圾自动回收机制,使得C++语言最头痛的内存管理的问题迎刃而解,垃圾回收可以有效防止内存泄漏,有效使用空闲内存。垃圾回收是基于JVM虚拟机实现的。那么java程序运行过程中,什么样的对象被判断为垃圾并进行自动回收的呢?

在JVM中,判断为垃圾的方法主要有以下两种:引用计数法和可达性分析法

引用计数是垃圾收集器中的早期策略。在这种方法中,堆中每个对象实例都有一个引用计数。当一个对象被创建时,就将该对象实例分配给一个变量,该变量计数设置为1。当任何其它变量被赋值为这个对象的引用时,计数加1(a = b,则b引用的对象实例的计数器+1),但当一个对象实例的某个引用超过了生命周期或者被设置为一个新值时,对象实例的引用计数器减1(如可达性算法 不可达 或 生命周期指:Eden区Survival1 Survival2来回移动达到最大年龄15时(-XX:MaxTenuringThreshold) 将被回收)。任何引用计数器为0的对象实例可以被当作垃圾收集。当一个对象实例被垃圾收集时,它引用的任何对象实例的引用计数器减1

优点:算法简单,容易实现;对程序需要不被长时间打断的实时环境比较有利。

缺点:如果对象被循环使用,算法难以检测;如父对象有对子对象的引用,子对象反过来引用父对象,这样他们的计数值可能永远不会为0.

可达性分析法:

7cf1e720589ab4add4e8d72a9e0d2229.png

通过一系列为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明该对象是不可用的。如果对象在进行可行性分析后发现没有与GC Roots相连的引用链,也不会理解死亡。它会暂时被标记上并且进行一次筛选,筛选的条件是是否与必要执行finalize()方法。如果被判定有必要执行finaliza()方法,就会进入F-Queue队列中,并有一个虚拟机自动建立的、低优先级的线程去执行它。稍后GC将对F-Queue中的对象进行第二次小规模标记。如果这时还是没有新的关联出现,那基本上就真的被回收。。那么什么样的对象是根对象呢:

主要有以下这些:

虚拟机栈中引用的对象;

方法区中常量引用的对象;

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

本地方法栈中引用的对象;

活跃的线程;

要宣告一个对象死亡,需要经过两次标记过程:

1、如果对象在根搜索后发现没有与可达对象有引用链,则进行第一次标记和筛选;筛选的条件是该对象是否有必要执行finalize()方法;当对象没有覆盖finalize()方法或虚拟机已经执行过finalize()方法,虚拟机会视为无必要执行回收;

2、如果对象有必要执行finalize()方法,则虚拟机会将对象放入一个队列中,并由虚拟机自动创建的,低优先级的线程执行finalize()方法。此时为对象最后一次逃脱被回收的机会,假如在执行finalize()方法时,对象能与引用链上的任何一个对象建立关联,则不被回收;否则该对象会被立即回收。

优点:可以解决循环引用的问题

缺点:多线程访问的环境下,其他线程可能会重复访问已回收的对象。

二、几种垃圾回收算法

1、标记-清除算法:

d60eb1a75bdcb27cad884dcfdd11e8e8.png

首先标记出需要回收的对象,标记完成后,统一回收所有已被标记的对象。

优点:不需要对象的移动,在存活对象比例高时,极为高效。

缺点:标记和清除的效率不高;需要一个空闲列表标记出所有的区域和大小,可能会产生大量的内存碎片。

ede9ac177de329d30946a668f81f37f6.png

2、标记-整理算法

88c3b51eddf3c44a5ab27127f33f0917.png

标记的过程与标记-清除算法的过程一样,但是标记-整理算法会将可达性对象移动到一端,然后直接清理边界以外的内存区域。

优点:使用简单,不再有内存碎片问题;

缺点:会移动对象的内存地址,这样他们的引用地址也要更新;后果是GC停顿的时间会增加。

15ffd3dd8ec1af843066577050ae3e9c.png

3、复制算法

5dac47887c1c792b53754c4338ac344d.png

将内存分为大小相等的两部分,每次只使用其中的一部分,当使用内存区域满后,将活着的对象移动到另一部分,将已使用过的内存区域全部清掉。

优点:在新生代中使用非常高效;不会有内存碎片问题;

缺点:需要一块较大的内存空间,不要在新生代以外的内存空间使用。

5a76b399c022404e6a46f2e6fc0d60de.png

4、分代收集算法

b7537f5df03fbacc5e0184f903d6d9e8.png

将堆内存划分为新生代、老年代和永久代;新生代进一步划分为eden和 survivor(由fromspace和tospace组成)区;由于不同的对象有不同的生命周期,可以将对象按代来进行存储和回收。

新生代:新生成的对象都是放在新生代中的,新生代进一步划分为eden 区和survivor区;survivor区又分为s1和s2区;eden、s1和s2的大小 默认按照8:1:1的比例进行分配。当对象生成存放eden区,如果eden区空间不够,会触发一次GC过程,将eden区的存活对象复制到s1区,清空eden区;当s1区也储存满时,将eden和s1的存活对象存入s2区;如此在s1和s2区循环,当s2不足以存放eden和s1的对象时,将存活对象存入老年代。存活的对象每经过一次GC,年龄增加1,默认年龄为15时,会将存活对象存入老年代空间。若老年代空间也满时,会触发FULL GC,也就是老年代和新生代都会进行垃圾回收。

老年代:在新生代中经过N次垃圾回收的存活对象会放入老年代中,大对象也会被直接存放老年代中;老年代的空间比新生代大很多(大概是2:1),老年代存放的大都是存活时间比较长的对象,老年代存储空间不够时,会触发FULL GC,

永久代:用于存放静态文件(class类和方法)和常量;永久代对垃圾回收没有显著影响,对永久代的垃圾回收主要是废弃的常量和无用的类。永久代在JAVASE 8的新特性中已经删除,取而代之的是元空间(meta space).

分代收集算法的特点是:

1、新对象优先分配在eden区中

2、大对象直接分配在老年代长期存活的对象分配进老年代中,默认15岁

3、

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值