深入理解JVM——虚拟机GC
对象是否存活
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的更多相关文章
- Java工程师学习指南第6部分:深入理解JVM虚拟机
本文整理了微信公众号[Java技术江湖]发表和转载过的JVM虚拟机相关优质文章,想看到更多Java技术文章,就赶紧关注本公众号吧吧. JVM原理分析,看了都说好 JVM 深入学习:Java 解析 Cl ...
- 深入理解JVM虚拟机11:Java内存异常原理与实践
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...
- 深入理解JVM虚拟机1:JVM内存的结构与消失的永久代
本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...
- 《深入理解JVM虚拟机》读书笔记
前言:<深入理解JVM虚拟机>是JAVA的经典著作之一,因为内容更偏向底层,所以之前一直没有好好的阅读过.最近因为刚好有空,又有了新目标.所以打算和<构架师的12项修炼>一起看 ...
- 进入JVM的世界:《深入理解JVM虚拟机》-- 思维导图
进入JVM的世界:<深入理解JVM虚拟机>-- 思维导图 之前一直都是零零散散的看了些JVM的知识,心想这样不行啊!于是便抽空看了一下这本神书,阅罢,醍醐灌顶.豁然开朗.真正的是知其然,更 ...
- 深入理解JVM虚拟机:(一)Java运行时数据区域
概述 JVM是Java语言的精髓所在,因为它Java语言实现了跨平台运行,以及自动内存管理机制等,本文将从概念上介绍JVM内存的各个区域,说明个区域的作用. JVM运行时数据区模型 Java虚拟机在执 ...
- 深入理解JVM虚拟机开篇:JVM介绍与知识脉络梳理
微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔 ...
- 推荐收藏系列:一文理解JVM虚拟机(内存、垃圾回收、性能优化)解决面试中遇到问题(图解版)
欢迎一起学习 <提升能力,涨薪可待篇> <面试知识,工作可待篇 > <实战演练,拒绝996篇 > 欢迎关注我博客 也欢迎关注公 众 号[Ccww笔记],原创技术文章 ...
- 深入理解JVM虚拟机(一):JVM运行时数据区
概述: JVM将内存的管理进行封装,使得开发人员不必关心内存申请.释放操作.但是在高级程序开发.复杂业务场景开发的时候,如果出现内存溢出的情况,对于开发人员而言就很难去分析出原因.所以还是很有必要去了 ...
随机推荐
- JAVA核心问题(一)反射之引言 构造函数
反射,简单来说,就是在运行时获取Class对象的所有属性和方法,无论公有私有.虽然是一个基础问题,在这里还是全面的记录一下,认真对待! 获取构造函数 构造函数大致分为两种,public和非public ...
- _ZNote_Qt_定时器的总结
Qt中实现定时器有两种方法. 一种是使用QObject类定时器;一种是使用QTimer类定时器.(定时器的精度依赖于操作系统和硬件,大多数平台支持20ms) 1,QObject类定时器. 通过QObj ...
- pyinstaller打包程序 带图片
首选说一下,这种打包方式只能在本电脑上使用运行正常 准备:.py文件:你的程序 gif文件:你要用的图片 第一步: 在上面文件所在目录打开cmd 输入 pyi-makespec FP.py 会生成一个 ...
- 普通java程序,maven打包
pom.xml文件: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://ww ...
- nlog 的手动配置
使用nlog的时候,有时候需要手动配置.比如数据库链接和密码不配在文件里,或者统计配置在一个位置之类的. var config = new NLog.Config.LoggingConfigurati ...
- mybatis环境配置与入门例子
1.jar包的导入 mybatis需要jar包:mybatis-3.4.6.jar mysql驱动jar包:mysql-connector-java-5.1.34.-bin.jar 日志记录jar包: ...
- hdoj6483 A Sequence Game(ST预处理RMQ+莫队)
传送:http://acm.hdu.edu.cn/showproblem.php?pid=6483 题意:有长度为$n$的数组,对于一个子区间$[l,r]$内,存在最大值$mx$与最小值$mi$,有$ ...
- scikit-image 图像处理库介绍
今天学习图像处理的时候,无意中看到了scikit 软件包,抱着学习的态度,这里做个记录,方便以后翻阅. 概念:scikit-image 是一种开源的用于图像处理的 Python 包.它包括分割,几何变 ...
- ubuntu里面搭建虚拟环境过程中遇到的问题以及解决方法。
今天开始学习Django,发现要搭建虚拟环境.就按照百度上面的方法在ubuntu中输入终端命名进行配置.发现自己是按照步骤来的.却总是在最后一步启动 source .bashrc 的时候出现''com ...
- Go 新起点
因项目需求 又得开始啃Go了,虽然比计划早了点,撸起袖子开始干吧~