对象是否存活

Java的GC基于可达性分析算法(Python用引用计数法),通过可达性分析来判定对象是否存活。这个算法的基本思想是通过一系列"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时(图论称之为不可达),则证明此对象是不可用的。

无论引用计数法,还是可达性分析都离不开“引用”的概念。Java将引用分为四种(强引用、软引用,弱引用,虚引用),这四种引用强度依次逐渐减弱。

  • strong reference强引用,垃圾收集器永远不会回收存在强引用的对象。(如Object obj = new object()就是一个强引用)

  • soft reference软引用,描述一些还有用但是并非必须的对象。系统将要发生内存溢出异常之前,将会把这些软引用对象列入回收范围中进行二次回收。如果这次回收之后还是不够内存,才会抛出内存溢出异常。

  • weak reference弱引用,也是用来描述非必须对象的,但是它的强度更弱。被弱引用的对象只能活到下一次GC发生之前。当GC发生,无论当前内存是否充足都会回收弱引用对象。

  • phantom reference虚引用(幽灵引用),是最弱的一种引用关系。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。

在可达性算法中不可达的对象,不会直接死亡,如果它还没有执行finalize方法,虚拟机将在一个优先级很低的线程中触发它的finalize方法(不保证完成),如果此时对象又将自己关联到引用链上,那么GC将把它移出“即将回收”的集合。

由于finalize方法只会被执行一次,所以这个“自救”过程也只会经历一次。

垃圾回收方法

1、标记清除法

首先标记所有需要回收的对象,然后统一回收所有要回收的对象。方法简单,但是效率不高,并且产生了很多内存碎片。

2、复制算法

将内存分为大小相等的两块,每次只使用一块。当一块的内存用完了,就将还存活的对象复制到另一块内存上,然后把使用过的那一半内存空间一次性清理掉。这样再分配内存时只需要移动堆顶指针,实现简单,运行高效。但是代价是实际可用内存缩小了一半,实在太高。

因为新生代大部分对象都是”朝生夕死“,所以虚拟机将内存分为1个Eden和2个Survivor空间,大小比例默认为8:1:1。当回收时,将Eden和Survivor中还存活着的对象复制到另一个Survivor空间上,然后清理掉Eden和刚才用过的Survivor空间。这样子就只需要牺牲10%的新生代内存。

分配担保:因为存在1个Survivor空间存放不了Eden和另一个Survivor空间的对象的情况,此时那些存活的对象由于无法放入该Survivor空间,将直接进入老年代。)

3、标记-整理算法

老年代对象存活率高,复制算法在这时候效率很低,而且老年代没有额外空间做分配担保,所以不适用。根据这种特点,有人提出了标记-整理算法。

首先标记所有要回收的对象,然后所有存活的对象都向一端移动,然后直接清理边界以外的内存。

4、分代收集算法

前面三个垃圾回收算法都算是铺垫,现在商业虚拟机的垃圾收集都采用”分代收集“。将Java堆分成新生代和老年代,新生代中每次GC有大量对象死去,只有少量存活,所以采用复制算法;老年代中对象存活率高,没有额外空间进行分配担保,就采用标记-清除或者标记-整理算法。

垃圾收集器

新生代:serial收集器

单线程,必须暂停其他所有工作线程(Stop The World),直到它收集结束。STW难以接受,但是简单高效。

新生代:parNew收集器

serial的多线程版本。server模式下首选,因为它可以和cms配合使用。

新生代:parallel scavenge收集器

专注于达到可控制吞吐量的收集器,主要适合后台运算而不用太多交互的任务。

老年代:serial old收集器

serial的老年代版本,单线程。使用标记-整理算法。

老年代:parallel old收集器

parallel scavenge的老年代版本。使用标记-整理算法。

老年代:cms收集器

经历四个阶段:

  • 初始标记:需要STW,仅仅标记一下GC Roots能直接关联的对象,所以很快。

  • 并发标记:进行GC Roots Tracing

  • 重新标记:需要STW,修正并发标记期间因程序运行导致标记产生变动的那一部分对象的标记记录,时间比初始标记长,但是远比并发标记短

  • 并发清除:清除对象

由于耗时最长的并发标记和并发清除过程,收集器线程都可以和用户线程一起工作,所以总体来说cms收集器的内存回收过程是和用户线程一起并发执行的,停顿很短。

缺点:cpu资源敏感,特别是cpu核数较少时;无法处理浮动垃圾;cms基于标记-清除算法,容易产生大量内存碎片。

G1收集器

最新的收集器,性能优秀,但是太复杂不再赘述。

内存分配和回收策略

  • 对象优先在Eden分配,如果启动了TLAB,将按照线程优先在TLAB上分配。

  • 当Eden不够空间时,虚拟机发起一次Minor GC(新生代GC)

  • 大对象直接进入老年代

  • 长期存活的对象将进入老年代(存活一次GC age+1,默认age 15时进入老年代)

  • Survivor空间中相同年龄所有年龄大小 的总和大于Survivor空间的一半 时,年龄大于等于该年龄的对象可以直接进入老年代

  • Minor GC时,虚拟机会检查老年代的剩余空间是否大于新生代对象总大小or历次晋升的平均大小,如果是那么就会进行Minor GC,否则认为老年代无法提供分配担保,进行Full GC。

深入理解JVM——虚拟机GC的更多相关文章

  1. Java工程师学习指南第6部分:深入理解JVM虚拟机

    本文整理了微信公众号[Java技术江湖]发表和转载过的JVM虚拟机相关优质文章,想看到更多Java技术文章,就赶紧关注本公众号吧吧. JVM原理分析,看了都说好 JVM 深入学习:Java 解析 Cl ...

  2. 深入理解JVM虚拟机11:Java内存异常原理与实践

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

  3. 深入理解JVM虚拟机1:JVM内存的结构与消失的永久代

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

  4. 《深入理解JVM虚拟机》读书笔记

    前言:<深入理解JVM虚拟机>是JAVA的经典著作之一,因为内容更偏向底层,所以之前一直没有好好的阅读过.最近因为刚好有空,又有了新目标.所以打算和<构架师的12项修炼>一起看 ...

  5. 进入JVM的世界:《深入理解JVM虚拟机》-- 思维导图

    进入JVM的世界:<深入理解JVM虚拟机>-- 思维导图 之前一直都是零零散散的看了些JVM的知识,心想这样不行啊!于是便抽空看了一下这本神书,阅罢,醍醐灌顶.豁然开朗.真正的是知其然,更 ...

  6. 深入理解JVM虚拟机:(一)Java运行时数据区域

    概述 JVM是Java语言的精髓所在,因为它Java语言实现了跨平台运行,以及自动内存管理机制等,本文将从概念上介绍JVM内存的各个区域,说明个区域的作用. JVM运行时数据区模型 Java虚拟机在执 ...

  7. 深入理解JVM虚拟机开篇:JVM介绍与知识脉络梳理

    微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔 ...

  8. 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)

    欢迎一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇 > <实战演练,拒绝996篇 > 欢迎关注我博客 也欢迎关注公 众 号[Ccww笔记],原创技术文章 ...

  9. 深入理解JVM虚拟机(一):JVM运行时数据区

    概述: JVM将内存的管理进行封装,使得开发人员不必关心内存申请.释放操作.但是在高级程序开发.复杂业务场景开发的时候,如果出现内存溢出的情况,对于开发人员而言就很难去分析出原因.所以还是很有必要去了 ...

随机推荐

  1. 【慕课网实战】Spark Streaming实时流处理项目实战笔记十七之铭文升级版

    铭文一级: 功能1:今天到现在为止 实战课程 的访问量 yyyyMMdd courseid 使用数据库来进行存储我们的统计结果 Spark Streaming把统计结果写入到数据库里面 可视化前端根据 ...

  2. 20154305 齐帅 PC平台逆向破解

    Exp1 PC平台逆向破解 一.实践目标 本次实践的对象是一个名为pwn1的linux可执行文件. 该程序正常执行流程是:main调用foo函数,foo函数会简单回显任何用户输入的字符串. 该程序同时 ...

  3. [solution] JZOJ-5795 词典

    [solution]JZOJ-5795 词典 题面 Description 小C有$n$个字符串$T_1 T_n$,给出$m$个询问 第$i$个询问给出一个字符串$S_i$,对于每个询问,我们可以得到 ...

  4. tab下图片要求

    下面是每个tab的属性: 属性 类型 必填 说明 pagePath String 是 页面路径,必须在pages中先定义 text String 是 tab上按钮文字 iconPath String ...

  5. VirtualBox中的快捷键

    VirtualBox中的快捷键 VirtualBox中的 Host 键默认是: Right Ctrl 键,意思是键盘上右边那个 “Ctrl”键. Host 键可以点击 Oracle VM Virtua ...

  6. Bootstrap框架的基本使用

    Bootstrap是什么 简介 就是已经写好的一个html和css的样式组合 Bootstrap是Twitter开源的基于HTML.CSS.JavaScript的前端框架. 它是为实现快速开发Web应 ...

  7. rabbitMq 初步

    RabbitMQ的工作原理 它的基本结构 组成部分说明如下: Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue. Exchange:消息队列交换机,按一定的规则将消息路 ...

  8. Info - 信息获取途径汇总

    目的驱动 大多数情况下,都是为了解决某个问题或完成某项任务,才需要进行针对性的.大范围的.细致化的信息获取. 那么,信息获取的方式和来源,就应该紧紧围绕这个"问题和任务"本身来确定 ...

  9. 卡尔曼滤波+单目标追踪+python-opencv

    很好的入门资料 向面试官一句话解释卡尔曼滤波: 用上一次的最优状态估计和最优估计误差去计算这一次的先验状态估计和先验误差估计: 用1得到的本次先验误差估计和测量噪声,得到卡尔曼增益: 用1,2步骤得到 ...

  10. Linux shell逐行读取文件的方法

    方法1:while循环中执行效率最高,最常用的方法. function while_read_line_bottom(){ while read line do echo $line done < ...