摘要:语言使用了内存动态分配和垃圾回收技术,掌握这些不仅可以提高自己的逼格,而且为后续的调优打下扎实的基础,让自己离架构师更近一步。任何引用计数器为的对象实例可以被当作垃圾收集。引用计数是垃圾收集器中的早期策略。
JVM之垃圾回收
市面上有关JVM垃圾回收的文章很多,有些是针对垃圾收集器,有些是介绍垃圾回收算法,也有些各方面都有涉及。本文希望能做一个比较全面的总结,最关键的是形成自己的语言,有自己的理解和沉淀。
一、为什么需要垃圾回收大家都知道,java语言的内存是动态分配的,不像C++语言还需要开发者专门干预,在一定程度上可以提高开发效率。那么大家可能疑问:既然不需要我们开发者关心,我们为什么还要讨论?作为一个程序语言的驾驭者,必然需要掌握该语言的方方面面,包括底层的原理和机制。
Java语言使用了内存动态分配和垃圾回收技术,掌握这些不仅可以提高自己的逼格,而且为后续的JVM调优打下扎实的基础,让自己离架构师更近一步。
JVM的内存结构包括五大区域:程序计数器、虚拟机栈、本地方法栈、堆区、方法区。其中程序计数器、虚拟机栈、本地方法栈3个区域随线程而生、随线程而灭,因此这几个区域的内存分配和回收都具备确定性,就不需要过多考虑回收的问题,因为方法结束或者线程结束时,内存自然就跟随着回收了。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正是垃圾收集器所需关注的部分。
垃圾收集器在对堆区和方法区进行回收前,首先要确定这些区域的对象哪些可以被回收,哪些暂时还不能回收,这就要用到判断对象是否存活的算法。
1、引用计数法
在这种方法中,堆中每个对象实例都有一个引用计数。任何引用计数器为0的对象实例可以被当作垃圾收集。
引用计数是垃圾收集器中的早期策略。该方法看似很实用,但是解决不了循环引用的问题(比如循环链表)。
2、可达性分析算法
从一个节点GC ROOT开始,寻找对应的引用节点,然后寻该引用节点的引用节点,当所有的引用节点寻找完毕之后,剩余没有被引用的节点将会被判定为是可回收的对象。
3、引用分类
在Java语言中,将引用又分为强引用、软引用、弱引用、虚引用四种,这四种引用强度依次逐渐减弱。垃圾回收算法都是基于强引用而言的。
三、垃圾回收算法1、标记-清除算法
标记-清除算法从根集合(GC Roots)开始扫描,对需要继续存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行回收,如下图所示。
标记-清除算法只需对不需要存活的对象进行处理,在存活对象比较多的情况下极为高效,但是会造成内存碎片。
2、复制算法
复制算法的提出就是为了解决内存碎片的问题,如下图所示。复制算法虽然解决了内存碎片的问题,但是浪费一半内存。另外在对象存活率很高的时候,复制成本会非常高。
3、标记-整理算法
标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题。具体流程见下图:
4、分代收集算法
一般情况下将堆区划分为老年代(Tenured Generation)和新生代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而新生代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。
新生代采用复制算法;老年代采用标记-整理算法。
其中年轻代内存分配过程如下:
1) 绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡;
2) 最初一次,当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0;
3) 下次Eden区满了,再执行一次Minor GC,将消亡的对象清理掉,将存活的对象复制到Survivor1中,然后清空Eden区;
4) 将Survivor0中消亡的对象清理掉,将其中可以晋级的对象晋级到Old区,将存活的对象也复制到Survivor1区,然后清空Survivor0区;
5) 然后跳到第三步,当两个存活区切换了几次(HotSpot虚拟机默认15次,用-XX:MaxTenuringThreshold控制)之后,仍然存活的对象,将被复制到老年代。
1、新生代收集器
1) Serial收集器
单线程收集器
2) ParaNew收集器
Serial收集器的多线程版,关注缩短垃圾收集时间。(使用-XX:+UseParNewGC开关来控制使用ParNew+Serial Old收集器组合收集内存;使用-XX:ParallelGCThreads来设置执行内存回收的线程数。)
3) Parallel Scavenge收集器
关注CPU吞吐量,即运行用户代码的时间/总时间。(使用-XX:+UseParallelGC开关控制使用Parallel Scavenge+Serial Old收集器组合回收垃圾(这也是在Server模式下的默认值);使用-XX:GCTimeRatio来设置用户执行时间占总时间的比例,默认99,即1%的时间用来进行垃圾回收;使用-XX:MaxGCPauseMillis设置GC的最大停顿时间;使用-XX:+UseAdaptiveSizePolicy可以进行动态控制Eden/Survivor比例,老年代对象年龄,新生代大小等。)
2、老年代收集器
1) Serial Old收集器
单线程收集器
2) Parallel Old收集器
Parallel Scavenge收集器的老年代版本(使用-XX:+UseParallelOldGC开关控制使用Parallel Scavenge +Parallel Old组合收集器进行收集。)
3) CMS收集器
多线程,优点是并发收集(用户线程可以和GC线程同时工作)
3、G1收集器
特性:
1) 首先收集尽可能多的垃圾(Garbage First)
内部采用了启发式算法,找出具有高收集收益的分区进行收集。
2) 并行和并发
和CMS相似,可以做到用户线程和GC线程同时工作。
3) 内存布局调整
将整个堆划分为多个大小相等的独立区域(Region),新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合。每个分区都可能随G1的运行在不同代之间前后切换。
G1划分了一个Humongous区,它用来专门存放巨型对象。
4) GC模式
G1提供了两种GC模式,Young GC和Mixed GC,两种都是Stop The World(STW)的。
[1] https://blog.csdn.net/ft30597...
[2] https://www.cnblogs.com/1024C...
[3] https://blog.csdn.net/x_i_y_u...
[4] https://www.jianshu.com/p/e99...
[5] https://blog.csdn.net/foolish...
[6] https://blog.csdn.net/coderli...
[7] https://www.cnblogs.com/ASPNE...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/72586.html
摘要:垃圾回收算法与垃圾回收器综述我们常说的垃圾回收算法可以分为两部分对象的查找算法与真正的回收方法。串行垃圾回收器一次只使用一个线程进行垃圾回收并行垃圾回收器一次将开启多个线程同时进行垃圾回收。 垃圾回收算法与 JVM 垃圾回收器综述归纳于笔者的 JVM 内部原理与性能调优系列文章,文中涉及的引用资料参考 Java 学习与实践资料索引、JVM 资料索引。 showImg(https://s...
摘要:年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年老代在年轻代中经历了次垃圾回收后仍然存活的对象,就会被放到年老代中。什么情况下触发垃圾回收由于对象进行了分代处理,因此垃圾回收区域时间也不一样。 [TOC] 与C/C++相比,java语言不需要程序员直接控制内存回收,java程序的内存分配和回收都是由JRE在后台自动进行,JRE会负责回收那些不再使用的内存,这种机制被称为垃圾...
摘要:直接对栈的操作只有两个,就是对栈帧的压栈和出栈。中将永久代移除,同时增加元数据区。在中,本地方法栈和虚拟机栈是在同一块儿区域,这完全取决于技术实现的决定,并未在规范中强制。 原文:https://github.com/linsheng97... 描述一下 JVM 的内存区域 程序计数器(PC,Program Counter Register)。在 JVM 规范中,每个线程都有它自己的...
摘要:概要要理解的内存管理策略,首先就要熟悉的运行时数据区,如上图所示,在执行程序的时候,虚拟机会把它所管理的内存划分为多个不同的数据区,称为运行时数据区。 这是一篇有关JVM内存管理的文章。这里将会简单的分析一下Java如何使用从物理内存上申请下来的内存,以及如何来划分它们,后面还会介绍JVM的核心技术:如何分配和回收内存。 JMM ( Java Memory Model )概要 show...
阅读 2244·2021-11-25 09:43
阅读 3434·2021-10-25 09:48
阅读 1306·2021-09-13 10:24
阅读 2721·2019-08-29 15:07
阅读 1224·2019-08-29 13:14
阅读 3232·2019-08-29 12:22
阅读 1330·2019-08-29 11:32
阅读 3209·2019-08-29 11:23