一、GC

  第一步:判断对象是否已死?有两种方法:第一种是引用计数法,即给对象添加一个引用计数器,当被引用时,计数器就+1;当引用失效时,就-1;当计数器为0时,代表对象没有被引用。但是计数器的缺点就是:对象之间相互引用时导致计数器不为零,无法被回收。第二种方法是可达性分析法,即通过定义一系列的GC Roots对象作为起始点,从这些起点向下搜索,当一个对象到GC Roots没有任何引用链时,则此对象是不可用的。

    可以作为GC Roots的对象包括:虚拟机栈中引用的对象、方法去种类静态属性或常量引用的对象,本地方法栈中native方法引用的对象。

    这里再说明一下引用!!!

    引用分为:强引用:把一个对象赋给一个引用变量,这个引用变量就是强引用。(强引用的对象不会被回收)

         软引用:用来描述一些还有用但非必需的对象。(在系统将要发生溢出异常之前,将这些对象列入回收范围进行二次回收,如果回收之后还没有足够内存,才会抛出异常。利用SoftReference类实现软引用)

         弱引用:用来描述非必需的对象,它的强度较软引用弱。(当发生GC时,无论当前内存是否足够,都会回收被弱引用关联的对象。利用WeakReference类来实现弱引用)

         虚引用:主要作用是跟踪对象被垃圾回收的状态,是最弱的引用关系。(PhantomReference类来实现虚引用)

  一个对象的死亡,至少要经历两次标记的过程:如果对象在进行可达性分析后发现没有与GC Roots相连的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。如果对象没有覆盖finalize()方法或者finalize方法已经被虚拟机调用过,则没有必要执行。如果这个对象被判定为有必要执行finalize方法,那么这个对象将会放置在一个叫做F-Queue的队列中,并在稍后由虚拟机自动建立一个低优先级的Finalizer线程去执行它。

  finalize方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次标记,如果对象重新与引用链的任何一个对象建立关联,那在第二次标记时会将它移除“即将回收”的集合;如果对象没有逃脱,那么它就真的被回收了。

  回收方法区:永久带的垃圾收集主要回收废弃常量和无用的类。判断一个类是不是“无用的类”条件:该类所有的实例都已经被回收;加载该类的ClassLoader已经被回收;该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类方法。

  第二步:垃圾收集算法

    第一种是标记-清除算法(mark-sweep),算法分为标记和清除两部分:首先标记出所有要回收的对象,标记完成后统一回收所有被标记的对象。缺点:一个是标记和清除两个过程效率不高,另一个是标记和清除之后产生大量不连续的空间碎片,导致以后分配较大对象时找不到足够的内存而提前触发GC。

    第二种是复制算法(copying),它是将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这一块内存用完后,就将还存活的对象复制到另一块上面,然后再把已使用过的内存一次清理掉。优点:解决了内存碎片化,实现简单,运行高效;缺点:将内存缩小为原来的一半,这样代价较大。

    第三种是标记-整理算法(mark-compact),分为标记和整理两部分,首先标记出所有要回收的对象,将所有存活的对象都向一端移动,然后直接清理掉端边以外的内存。

    第四种是分代收集算法,就是根据对象存货周期的不同将内存划分为新生代和老年代,然后根据每个年代特性采用合适的算法。在新生代中每次都有大量对象死去,只有少量存活,那么就采用复制算法;而老年代对象存活率高没有额外空间对它进行担保,就必须使用标记-清除或标记-整理算法来回收。

  第三步:垃圾收集器

    Serial收集器,是单线程收集器,它进行GC时必须暂停其他所有的工作线程,直到它收集结束,这项工作实际是虚拟机在后台自动发起和自动完成的。

    ParNew收集器,就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集之外,其余行为与Serial收集器一样。

    Parallel Scavenge收集器,是使用复制算法的新生代收集器,又是并行的多线程收集器。它的目的是达到一个可控制的吞吐量,高效率地利用CPU时间, PS收集器提供了控制最大垃圾收集停顿时间的 -XX:MaxGCPauseMillis参数和直接设置吞吐量大小的 -XX:GCTimeRatio参数用于精确控制吞吐量。(PS收集器还有一个参数-XX:+UseAdaptiveSizePolicy,这是一个开关参数,这个开关打开后,就不用手工指定新生代的大小、Eden和Survivor区的比例、晋升老年代对象的大小等细节参数,虚拟机会根据当前系统的运行情况收集性能监控信息动态调整这些参数,这种调节方式称为GC自适应的调节机制)。

    Serial Old收集器,是单线程收集器,使用标记-整理算法,是Serial收集器的老年代版本,是给Client模式下的虚拟机使用。

    Paralled Old收集器,是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法。

    CMS收集器,是一种以获取最短回收停顿时间为目标的收集器,使用多线程和标记-清楚算法。

      运作过程分为:初始标记(仅仅只是标记一下GC Roots能直接关联到的对象)

             并发标记(进行GC Roots Tracing 的过程)

             重新标记(为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录)

             并发清除(清除GC Roots不可达的对象,不需要暂停工作线程)。

      优点:并发收集、低停顿;

      缺点:CMS收集器对CPU资源非常敏感、CMS收集器无法处理浮动垃圾、基于标记-清除算法会产生大量的空间碎片。

    G1收集器,是一款面向服务端的垃圾收集器,G1具备并行与并发、分代收集、空间整合(基于标记-整理和复制算法实现,不会产生空间碎片)、可预测停顿的特点;G1收集器中它将Java堆划分为多个大小相等的独立区域,独立使用和回收,这样可以控制一次回收多个区域,减少一次GC所产生的停顿(区域划分机制)。G1之所以能建立可预测的停顿模型,是因为G1跟踪各个区域里面的垃圾堆积的价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的区域(优先区域回收机制)。为了避免全堆扫描,G1使用Remembered Set来管理相关的对象引用信息。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

      运作过程分为:初始标记(仅仅只是标记一下GC Roots能直接关联的对象,并修改TAMS的值,让下一阶段用户程序并发运行时,能在正确可用的region中创建对象,此阶段需要停顿线程,但时间很短)

             并发标记(从GC Root开始对堆中的对象进行可达性分析,找出存活的对象,耗时长,但可与用户线程并发执行)

             最终标记(为了修正并发标记期间因用户程序继续运行而导致标记产生变动的那一部分标记记录并把Remembered Set Logs的数据合并到Remembered Set 中,此阶段需要停顿线程,但可并发执行)

             筛选回收(首先根据各个region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划)

    并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。

    并发(Concurrent):指用户线程与垃圾收集线程同时执行。

二、内存分配与回收策略

  大多数情况下,对象分配在新生代的Eden区上,分配规则是不固定的,取决于当前使用的垃圾收集器以及虚拟机中与内存相关的参数设置。

  新生代:用来存放新产生的对象,占用1/3空间;新生代分为一个Eden区和两个Survivor区(FromSurvivor和ToSurvivor);当Eden区没有足够空间进行分配时,虚拟机将会进行一次Minor GC。进行一次Minor GC后的对象进入FromSurvivor区,当From和Eden区没有可用空间时,会进行第二次Minor GC,将存活的对象移到ToSurvivor区。

  大对象(需要大量连续内存空间的对象)直接进入老年代;虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配,这样做避免了在Eden区及两个Survivor区之间发生大量的内存复制。长期存活的对象将进入老年代,如果对象在Eden区出生并且经过第一次Minor GC后仍然存活,将被移动到Survivor区,并且对象年龄设为1。对象在Survivor区中每经过一次Minor GC,年龄就+1,当它的年龄增加到一定程度时(默认为15),就将会被晋升到老年代中。对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

  动态对象年龄判定:如果在Survivor区中相同年龄的所有对象大小总和大于Survivor区的一半,年龄大于或等于该年龄的对象可以直接进入老年代。

  空间分配担保:当出现大量对象在Minor GC后仍然存活就需要老年代进行分配担保,把Survivor区无法容纳的对象直接进入老年代中。前提是老年代本身有容纳这些对象的剩余空间。

  在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总大小或者历次晋升到老年代对象的平均大小,如果大于就进行MinorGC,否则将进行FullGC。

GC与内存分配策略的更多相关文章

  1. 【Java杂货铺】JVM#Java高墙之GC与内存分配策略

    Java与C++之间有一堵由内存动态分配和垃圾回收技术所围成的"高墙",墙外的人想进去,墙外的人想出来.--<深入理解Java虚拟机> 前言 上一章看了高墙的一半,接下 ...

  2. GC之一--GC 的算法分析、垃圾收集器、内存分配策略介绍

    一.概述 垃圾收集 Garbage Collection 通常被称为“GC”,它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本 ...

  3. 《深入理解Java虚拟机》内存分配策略

    上节学习回顾 1.判断对象存活算法:引用计数法和可行性分析算法 2.垃圾收集算法:标记-清除算法.复制算法.标记-整理算法 3.垃圾收集器: Serial:新生代收集器,采用复制算法,单线程. Par ...

  4. 深入理解java虚拟机(2)------垃圾收集器和内存分配策略

    GC可谓是java相较于C++语言,最大的不同点之一. 1.GC回收什么? 上一篇讲了内存的分布. 其中程序计数器栈,虚拟机栈,本地方法栈 3个区域随着线程而生,随着线程而死.这些栈的内存,可以理解为 ...

  5. JVM学习总结四——内存分配策略

    之前几篇我们介绍了jvm的内存模型以及垃圾回收机制,而本篇我们将介绍几个JVM中对象在分配内存是应该遵循的策略.毕竟,想要去优化程序,不仅要考虑垃圾回收的过程,还要从对象内存分配的角度减少gc的代价. ...

  6. java虚拟机学习-JVM内存管理:深入垃圾收集器与内存分配策略(4)

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 说起垃圾收集(Garbage Collection,下文简称GC),大部分人都把这项 ...

  7. jvm系列 (二) ---垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 前言:本文基于<深入java虚拟机>再加上个人的理解以及其他相关资料,对内容进行整理浓缩总结.本文中的图来自网络,感谢图的作者.如果有不正确的地方,欢迎指出. 目 ...

  8. Java 内存分配策略

    内存有分配,就有回收,Java 的 GC 算法在前一篇文章中已经介绍过了,这篇文章着重介绍 Java 的内存分配策略. 从大方向讲,除去 JIT ,对象的内存分配就是在堆上分配,对象主要分配在新生代的 ...

  9. 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略

    1.  前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...

随机推荐

  1. Vue + TypeScript 踩坑总结

    vue 和 TypeScript 结合的情况下,很多写法和我们平时的写法都不太一样,这里总结我项目开发过程中遇到的问题和问题的解决方案 有些问题可能还没解决,欢迎各位大佬给与提点. 另外,使用本文前可 ...

  2. 失去循环标记的Python,我这样实现跳出外层循环

    不完美的Python 自从各类Python大火,感觉天上地下哪儿都有Python的一席之地,Python功夫好啊-但python有些细节上缺少其他语言的便利.今天我们就来举几个例子. 跳出外层循环 大 ...

  3. 使用react-breadcrumbs-dynamic

    这是完全独立于路由器的解决方案,你可以将其与任何版本的React Router(2或3或4)或任何其他用于React的路由库一起使用,或者完全不进行路由.您只需要指定面包屑项目及其道具的组件.然而道具 ...

  4. .Net Core使用Ocelot网关(一) -负载,限流,熔断,Header转换

    1.什么是API网关 API网关是微服务架构中的唯一入口,它提供一个单独且统一的API入口用于访问内部一个或多个API.它可以具有身份验证,监控,负载均衡,缓存,请求分片与管理,静态响应处理等.API ...

  5. mysql主从复制原理及实践

    Mysql主从复制原理及实践 mysql主从框架       MySQL主从架构是MySQL集群中最基本也是最常用的一种架构部署,能够满足很多业务需求,常见的有一主一从或者一主多从.可以防止单一主机的 ...

  6. HDU3870-Caught these thieves(最小割-&gt;偶图-&gt;最短路问题)

    A group of thieves is approaching a museum in the country of zjsxzy,now they are in city A,and the m ...

  7. 【CSS】333- 使用CSS自定义属性做一个前端加载骨架

    点击上方"前端自习课"关注,学习起来~ 我们在打开APP或者网站的时候,经常可以看到这样的效果,在内容加载完成之前,会有一个骨架动画的出现,这种加载方式比传统的进度条方式要友好的多 ...

  8. Hybird App 应用开发中5个必备知识点复习

    前言 我们大前端团队内部 ?每周一练 的知识复习计划还在继续,本周主题是 <Hybird APP 混合应用专题> ,这期内容比较多,篇幅也相对较长,每个知识点内容也比较多. 之前分享的每周 ...

  9. Vue ---- 组价 组件化 子传父 父传子

    目录 补充js的for循环: 组件 1.组件的分类: 2.组件的特点 3.创建局部组件 4.全局组件 二.组件化 一.组件传参父传子 二.组件传参:子传父 补充js的for循环: // for in遍 ...

  10. JAVA 使用jgit管理git仓库

    最近设计基于gitops新的CICD方案,需要通过java读写git仓库,这里简单记录下. JGit是一款pure java的软件包,可以读写git仓库,下面介绍基本使用. 引入jgit maven引 ...