热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于java:JVM知识总结3垃圾收集策略与算法

程序计数器、虚拟机栈、本地办法栈随线程而生,也随线程而灭;栈帧随着办法的开始而入栈,随着办法的完结而出栈。这几个区域的内存调配和回收都确定性,在这

【JVM常识总结-1】JVM内存模型
【JVM常识总结-2】HotSpot虚拟机对象
【JVM常识总结-3】垃圾收集策略与算法

程序计数器、虚拟机栈、本地办法栈随线程而生,也随线程而灭;栈帧随着办法的开始而入栈,随着办法的完结而出栈。这几个区域的内存调配和回收都确定性,在这几个区域不须要过多的思考回收的问题,因为办法完结或者线程完结,内存天然就跟随着回收了。
而对于Java堆和办法区,咱们只有在程序运行期间能力晓得会创立哪些对象这部分内存的调配和回收都是动静的,垃圾回收器所关注的正是这一部分内存。

断定对象是否存活

若一个对象不被任何对象或变量援用,那么它就是有效对象,须要被回收。

援用计数法

在对象头维护者一个counter计数器,对象被援用一次则计数器+1;若援用时效则计数器-1。当计数器为0时,就认为该对象有效了。
援用计数算法的实现简略,断定效率也很高,在大部分状况下它都是一个不错的算法。然而支流的Java虚拟机里没有选用援用计数算法来治理内存,次要是因为它很难解决对象之间循环援用的问题。(尽管循环援用的问题能够通过Recycler算法解决,然而在多线程环境下,援用计数变更也要进行低廉的同步操作,性能较低,晚期的编程语言会采纳此算法。)

举个例子:对象objA和objB都有字段instance,令objA.instance = objB并且objB.instance = objA,因为他们相互援用着对方,导致他们的援用计数器都不为0,于是援用计数器算法无奈告诉GC收集器回收它们。

可达性分析法

所有和GC Roots间接或间接关联的对象都是无效对象,和GC Roots没有关联的对象就是有效对象。
GC Roots是指:

  • Java虚拟机栈(战神中的本地变量表)中援用的对象。
  • 本地办法中援用的对象
  • 办法区中常量援用的对象
  • 办法去中动态属性援用的对象

GC Roots并不包含堆中对象所援用的对象,这样就不会有循环援用的问题。

援用的品种

断定对象是否存在活与“援用”无关。在JDK1.2以前,Java中的援用定义很传统,一个对象只有被援用或者没有被援用两种状态,咱们心愿能形容这一景象:当内存空间还足够时,则多保留在内存中;如果内存空间在进行垃圾收集后还十分缓和,则能够摈弃这些对象。很多零碎的缓存性能复合这样的应用场景。
在JDK1.2之后,Java对援用的概念进行了裁减,将援用分为了一下四种。不同的援用类型,次要体现的是对象不同的可达性状态reachable和垃圾收集的影响。

强援用(Strong Reference)

相似Object obj = new Object()这类的援用,就是强援用,只有强援用存在,垃圾回收器永远不会回收被援用的对象。然而,如果咱们谬误的放弃了强援用,比方:赋值给了static变量,那么对象在很长一段时间内不会被回收,会产生内存泄露。

软援用(Soft Reference)

软援用是一种绝对强援用弱化一些的援用,能够让对象能豁免一些垃圾回收器,只有当JVM认为内存不足时,才会去试图回收软援用指向的对象。JVM会确保在抛出OutOfMemeryError之前,清理软援用指向的对象。软援用通常用来实现内存敏感的缓存,如果还有闲暇内存,就能够临时保留缓存,当内存不足时清理掉,这样就保障了应用缓存的同时,不会耗尽内存。

弱援用(Weak Reference)

弱援用的强度比软援用更弱一些。当JVM进行垃圾回收时,无论内存是否短缺,都会回收只被弱援用关联的对象。

虚援用(Phantom Reference)

虚援用也称幽灵援用或者幻影援用,它是最弱的一种援用关系。一个对象是否有虚援用的存在,齐全不会对其生存工夫形成影响。它仅仅是提供了一种确保对象被finalize当前,做某些事件的机制,比方,通常用来做所谓的Post-Mortem清理机制。

回收堆中有效对象

对于可达性剖析中不可达的对象,也并不是没有存活的可能。

断定finalize()是否有必要执行

JVM会判断此对象是否有必要执行finalized()办法,如果对象没有笼罩finalized()办法,或者finalize()办法曾经被虚拟机调用过,那么视为“没有必要执行”。那么对象基本上就真的被回收了。
如果对象被断定为有必要执行finalize()办法,那么对象会被放入一个F-Queue队列中,虚构机会以比拟低的优先级执行这些finalize()办法,但不会确保所有的finalize()办法都会执行完结。如果finalize()办法呈现耗时操作,虚拟机就间接进行指向该办法。将对象革除。

对象新生或死亡

如果在执行finalize()办法时,将this赋给了某一个援用,那么该对象就新生了。如果没有,那么就会被垃圾收集器革除。

任何一个对象的finalize()办法只会被零碎主动调用一次,如果对象面临下一次回收,它的finalize()办法不会被再次执行,想持续在finalize()中自救就生效了。

回收办法区内存

办法区中寄存生命周期较长的类信息、常量、动态变量,每次垃圾收集只有大量的垃圾被革除。办法区中次要革除两种垃圾:

  • 废除常量
  • 无用的类

断定废除常量

只有常量池中的常量不被任何变量或对象援用,那么这些常量就会被革除掉。比方,一个字符串“bingo”进入了常量池,然而当期零碎没有任何一个String对象援用常量池中的“bingo”常量,也没有其余中央援用这个字面量,必要的话,“bingo”常量会被革除出常量池。

断定无用的类

断定一个类是否是“无用的类”,条件极为刻薄。

  • 该类的所有对象都曾经被革除
  • 加载该类的ClassLoader曾经被回收
  • 该类的java.lang.Classd对象曾经没有在任何中央被援用,无奈在任何中央通过反射的形式拜访该类的办法。

一个类被虚拟机加载进办法区,那么在堆中就会有一个代表该类的对象:java.lang.Class。这个对象在类别加载进办法区时被创立,在办法区该类被删除时革除。

垃圾收集算法

学会了如何断定有效对象、无用类、废除常量之后,残余的工作就是回收这些垃圾。常见的垃圾收集算法有一下几个:

标记-革除算法

标记:遍历所有GC Roots,而后将所有GC Roots可达的对象标记为存活的对象。

【留神】标记的是存活的不须要回收的对象!!!

革除:革除的过程将遍历堆中所有的对象,将没有标记的对象全副革除掉。与此同时,革除哪些被标记过的对象的标记,以便下次的垃圾回收。
这种办法有两个有余:

  • 效率问题:标记和革除两个过程的效率都不高。
  • 空间问题:标记革除之后会产生大量不间断的内存碎片。碎片太多可能导致当前须要调配较大对象时无奈找到足够的间断内存而不得不提前触发另一次的垃圾收集动作。

复制算法

为了解决效率问题,“复制”收集器算法呈现了。它将可用内存按容量划分为大小相等的两块,每次只应用其中的一块。当这一块内存用完,须要进行垃圾回收时,就将存活者的对象复制到另一块下面,而后将第一款内存全副革除。这种算法有劣势有劣:

  • 长处:不会有内存碎片的问题。
  • 毛病:内存放大为原来的一半,节约空间。

为了解决空间利用率问题,能够将内存分为三块:Eden/From Survivor/To Survivor,比例是8:1:1,每次应用Eden和其中一块Survivor。回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间上,最初清理掉Eden和方才应用的Survivor空间。这样只有10%的内存被节约。
然而咱们无奈保障每次回收都只有不多于10%的对象存活,当Survivor空间不够时,须要依赖其余内存(指老年代)进行调配担保。

调配担保

为对象分配内存空间时,如果Eden+Survivor中闲暇区域无奈装下该对象,会触发MinorGC进行垃圾收集。但如果Minor GC过后仍然有超过10%的对象存活,这样存活的对象间接通过调配担保机制进入老年代,而后再将新对象存入Eden区。

标记-整顿法(老年代)

标记:它的第一个阶段与标记-革除算法是截然不同的,均是遍历GC Roots,而后将存活的对象标记。
整顿:挪动所有存活的对象,且依照内存地址秩序顺次排列,而后将末端的内存地址当前的内存全副回收。因而,第二阶段才称为整顿阶段。
这是一种老年代的垃圾收集算法。老年代的对象个别寿命比拟长,因而每次垃圾回收会有大量对象存活,如果采纳复制算法,每次须要复制大量存活的对象,效率很低。

分代收集算法

依据对象存活周期的不同,将内存划分为几块。个别是把Java堆内分为新生代和老年代,针对各个年代的特点采纳最合适的收集算法。

  • 新生代:复制算法
  • 老年代:标记-革除算法、标记-整顿算法

推荐阅读
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • 深入理解Java多线程并发处理:基础与实践
    本文探讨了Java中的多线程并发处理机制,从基本概念到实际应用,帮助读者全面理解并掌握多线程编程技巧。通过实例解析和理论阐述,确保初学者也能轻松入门。 ... [详细]
  • 深入剖析JVM垃圾回收机制
    本文详细探讨了Java虚拟机(JVM)中的垃圾回收机制,包括其意义、对象判定方法、引用类型、常见垃圾收集算法以及各种垃圾收集器的特点和工作原理。通过理解这些内容,开发人员可以更好地优化内存管理和程序性能。 ... [详细]
  • 并发编程:深入理解设计原理与优化
    本文探讨了并发编程中的关键设计原则,特别是Java内存模型(JMM)的happens-before规则及其对多线程编程的影响。文章详细介绍了DCL双重检查锁定模式的问题及解决方案,并总结了不同处理器和内存模型之间的关系,旨在为程序员提供更深入的理解和最佳实践。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本文作者分享了在阿里巴巴获得实习offer的经历,包括五轮面试的详细内容和经验总结。其中四轮为技术面试,一轮为HR面试,涵盖了大量的Java技术和项目实践经验。 ... [详细]
  • JavaScript 基础语法指南
    本文详细介绍了 JavaScript 的基础语法,包括变量、数据类型、运算符、语句和函数等内容,旨在为初学者提供全面的入门指导。 ... [详细]
  • 深入解析Spring启动过程
    本文详细介绍了Spring框架的启动流程,帮助开发者理解其内部机制。通过具体示例和代码片段,解释了Bean定义、工厂类、读取器以及条件评估等关键概念,使读者能够更全面地掌握Spring的初始化过程。 ... [详细]
  • 本文详细探讨了Java中的ClassLoader类加载器的工作原理,包括其如何将class文件加载至JVM中,以及JVM启动时的动态加载策略。文章还介绍了JVM内置的三种类加载器及其工作方式,并解释了类加载器的继承关系和双亲委托机制。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • andr ... [详细]
author-avatar
米粒尖尖果儿_445
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有