JVM之GC(二)
昨天总结了GC之前要做的事情,今天介绍一下主流的GC算法。
先介绍一下几个名词:
Stop The World(STW):JVM进行GC的时候总不能一边清理垃圾一边制造垃圾把,那么垃圾鉴定的准确性根本无法得到保证,所以需要将服务全部停掉那么一瞬间;
Yong GC、Minor GC:年轻代区域的GC,所有的年轻代GC都会触发STW;
Old GC、Major GC:老年代区域的GC,只有串行部分会触发STW;
Full GC:主要针对老年代区域而言的串行GC,会触发STW。
首先说一下年龄分代收集算法,根据对象的存活周期,将堆区分为年轻代(新生代)和老年代。顾名思义,刚生成的对象放入年轻代,勤用勤收集,对象成功经历几轮GC后仍存活下来,便将它移入到老年代中去,如果老年代也满了,那么会触发Full GC。如果你是JVM设计者,你会怎么分配年轻代和老年代的比重呢?老年代收集效率差,存活率高,轻易不要移动这里的对象,如果老年代空间不充足,便会频繁触发Full GC,得不偿失;如果老年代分配过多也不好,对象全都跑到老年代了,长时间不触发清理操作会造成过于碎片化,严重导致空有内存却无法分配的现象。因此堆区应该为老年代和年轻代分配合适的比例,一般为4:1(调优参数:-XX:NewRatio=4)
实际上大多数的JVM都是以年龄分代算法为基础,将堆区划分成独立的两个区域,然后再结合其他的GC算法分开进行GC,从而更高效。
最先提出来的是标记清除法:先将堆区的需要回收的对象标记下来,然后统一回收。它会产生两个问题,首先是效率低下,标记和清理过程的效率都是很低的;然后是碎片问题,剩余的存活的对象分散各处,造成堆区碎片化,不方便为大对象分配内存。
针对上面两个问题,提出了复制算法,把堆区分为一个eden区域和两个小的survivor区域,当需要申请内存时,JVM会在较大的eden区域为其分配空间,当eden区域无法为新对象提供内存时,JVM将其中一个survivor空间和eden空间中存活的对象移到另一个survivor中,然后清空eden和之前使用过的survivor,按此循环,也就是说,总有一个survivor区域是留空的,用来存放其余二者存活的对象。
将上面两个方法对比来看,复制算法更适合用来处理存活率低的内存区域,也就意味着复制量小,更高效,显而易见,它是一个年轻代算法;相对而言,如果用标记清除法处理年轻代,使这片空间过于碎片化,当eden为较大对象分配空间时便会很乏力(实际上大对象或大数组是直接在老年代分配的),而且年轻代应该像他的名字一样高效、快速GC,该算法的标记和清除效率并不高,因此标记清除法更应该拿来处理老年代。
针对标记清除法的碎片化问题以及老年代的特点,有人又提出了一个改进措施,标记整理法,它的思想是:让存活的对象向一端移动,然后从边界处将另一侧所有空间直接清理掉。
前面提到了标记,内存中这么多引用变量,JVM如何寻找它是对象引用类型呢?其实,JVM使用了一组OopMap的数据结构来存放引用类型,在JIT编译和类加载的时候记录下来那些位置是引用,这样GC的时候,JVM就可以快速且准确的完成GC Roots的枚举了。
到这里,又引出了另外一个问题,变量、对象间的引用关系是一直在变化的,JVM不可能监听每一条指令,代价太大。所以引出了安全点这个概念,如果在安全点位置上我们对OopMap进行校准的话,那么JVM是可以在这个位置进行GC的,此时STW之后的引用类型是准确的。那么什么位置可以作为安全点呢?1、所有指令的末尾均可添加 2、方法调用、循环回跳之前等可添加; 它的选定是依据“程序在此处是否会长时间执行”为标准的。
对于多线程的程序,STW的时候是需要全部暂停的,但我们并不能保证多个线程正在执行的指令处是否有安全点,上一段已经说明只有安全点处才可以安全准确的GC。这里有两种解决方案:
1、抢先式中断:JVM觉得可以GC了,那就先STW,然后检查所有线程是否在安全点上,不在的话就让它继续执行;很少JVM会采用这种方式
2、主动式中断:在安全点上加一个轮询的标志,在需要GC时,将该标志设为中断值,已经在安全点上的线程便会轮询等待,未在安全点上的线程读取不到这个标志会继续执行
如果一个线程没有分配到时间片,线程出于sleep或blocked状态如何处理呢?JVM不可能等到它分配时间片之后在GC把。因此提出了安全区域这个概念,只有这段指令不会导致引用变化的片段才可以作为安全区域使用,在GC时JVM会忽视掉处于安全区域的线程,在这个区域内的任何地方开始GC都是安全的。当线程执行到安全区域的代码时,首先标识自己进入了安全区域。如果它要走出安全区域,需要先检查JVM是否处于GC状态且是否已经完成了根节点枚举,如果满足了根节点枚举或不在GC状态,它就可以走出来继续执行代码,否则他必须得等待知道收到信号为止。
JVM之GC(二)的更多相关文章
- 深入JVM系列(二)之GC机制、收集器与GC调优
一.回想JVM内存分配 须要了解很多其它内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配 2.大对象直接进入老年代 3.长期存活的 ...
- 深入JVM系列(二)之GC机制、收集器与GC调优(转)
一.回顾JVM内存分配 需要了解更多内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配2.大对象直接进入老年代 3.长期存活的对象 ...
- JVM的GC概述
JVM的GC概述 GC即垃圾回收,是指jvm用于释放那些不再使用的对象所占用的内存.在充分理解了垃圾收集算法和执行过程后,才能有效的优化它的性能. 有些垃圾收集专用于特殊的应用程序.比如,实时应用程序 ...
- JVM 系列(二)内存模型
02 JVM 系列(二)内存模型 一.JVM 内存区域 JVM 会将 Java 进程所管理的内存划分为若干不同的数据区域.这些区域有各自的用途.创建/销毁时间: 一. 线程私有区域 线程私有数据区域生 ...
- JVM之GC算法的实现(垃圾回收器)
上一节:<JVM之GC算法> 知道GC算法的理论基础,我们来看看具体的实现.只有落地的理论,才是真理. 一.JVM垃圾回收器的结构 JVM虚拟机规范对垃圾收集器应该如何实现没有规定,因为没 ...
- 聊一聊 JVM 的 GC
原文链接:https://www.changxuan.top/?p=1457 引言 JVM 中的 GC 在技术博客中应该算是个老生常谈的话题,网络上也存在着许多质量参差不齐的文章,可以看出来大都是&q ...
- Linux使用jstat命令查看jvm的GC情况
Linux使用jstat命令查看jvm的GC情况 http://www.open-open.com/lib/view/open1390916852007.html http://www.aiuxian ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- poptest老李谈jvm的GC
poptest老李谈jvm的GC poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.如果对课程感兴趣,请大家咨询qq:90882 ...
随机推荐
- ArrayList存储基本类型时的封装类
- 你看Http的 三次握手
你看Http的 三次握手 按层次分,TCP位于传输层,而且TCP协议能够确认数据是否送达到对方,所以在客户端请求资源的时候,你得让俺知道咱俩关系是不是已经确定了啊,对不.这跟谈恋爱一样一样的,得先确定 ...
- js后端返回一个时间戳,用原生怎么对时间进行格式化?
function fn(time) { var date = new Date(time); var len = time.toString().length; // 时间戳不足13位则在后面加零 i ...
- vue tab栏缓存解决跳转页面后返回的状态保持
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8& ...
- Python--day70--ORM一对一表结构
ORM一对一表结构:
- uni-app学习记录02-属性绑定.for循环
<template> <view class="content"> <text> 我是首页 </text> <!-- 输出纯字 ...
- H3C Easy IP配置举例
- Vue的Router路由传参
一.文件结构 二.vue.js 打开此链接 https://cdn.bootcss.com/vue/2.6.10/vue.js 复制粘贴页面的所有内容 三.vue-router.js 打开此链接 h ...
- linux模块参数
驱动需要知道的几个参数因不同的系统而不同. 从使用的设备号( 如我们在下一章见到的 ) 到驱动应当任何操作的几个方面. 例如, SCSI 适配器的驱动常常有选项控制标记命令队列 的使用, IDE 驱动 ...
- 【7003】&&【a203】合并多项式
Time Limit: 3 second Memory Limit: 2 MB 问题描述 求两个一元多项式的和.输入多项式方式为:多项式项数.每项系数和指数,按指数从大到小的顺序输入.输出多 ...