java的前生今世_HBaseGC的前生今世-身世篇

网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术,提供稳定流畅、低时延、高并发的视频直播、录制、存储、转码及点播等音视频的PAAS服务,在线教育、远程医疗、娱乐秀

网易视频云是网易倾力打造的一款基于云计算的分布式多媒体处理集群和专业音视频技术,提供稳定流畅、低时延、高并发的视频直播、录制、存储、转码及点播等音视频的PAAS服务,在线教育、远程医疗、娱乐秀场、在线金融等各行业及企业用户只需经过简单的开发即可打造在线音视频平台。现在,网易视频云的技术专家给大家分享一则技术文:HBase GC的前生今世 - 身世篇。

在之前的HBase BlockCache系列文章中已经简单提到:使用LRUBlockCache缓存机制会因为CMS GC策略导致内存碎片过多,从而可能引发臭名昭著的Full GC,触发可怕的’stop-the-world’暂停,严重影响上层业务;而Bucket Cache缓存机制因为在初始化的时候就申请了一片固定大小的内存作为缓存,缓存淘汰不再由 JVM管理,数据Block的缓存操作只是对这篇空间的访问和覆盖,因而大大减少了内存碎片的出现,降低了Full GC发生的频率。那CMS

GC策略如何导致内存碎片过多?内存碎片过多如何触发Full GC?HBase在演进的道路上又如何不断优化CMS GC?接下来这个系列《HBase GC的前生今生》将会为你一一揭开谜底,这个系列一共两篇文章,本篇文章-’身世篇’将会带你全面了解HBase的GC机制,后面一篇-’演进篇’将会给你道出HBase在发展的道路上如何不断对Full GC进行优化。

Java GC概述

整个HBase是构建在JVM虚拟机上的,因此了解HBase的内存管理机制以及不同缓存机制对GC的影响,就必须对Java GC有一个全面的了解。至于深入地理解Java GC 的工作原理,不在本文的讨论范围之内;当然,如果已经对Java GC比较熟悉,也可以跳过此节。

Java GC建立在这样一个假设基础上的:大多数内存对象要么生存周期比较短,很快就会没人引用,比如处理RPC请求的buffer可能只会生存几微秒;要么生存周期比较长,比如Block Cache中的热点Block,可能就会生存几分钟,甚至更长时间。基于这样的事实,JVM将整个堆内存分为两个部分:新生代(young generation)和老生代(tenured generation),除此之外,JVM还有一个非堆内存区-Perm区,主要存放class信息以及其他meta元信息,内存结构如下图所示:

0R91523W-0.png

其中Young区又分为Eden区和两个Survivor 区:S0和S1。一个内存对象在创建之后,首先会为其在新生代申请一块内存空间,如果这个对象在新生代存活了很长时间,会将其迁移到老生代。 在大多数对延迟敏感的业务场景下(比如HBase),建议使用如下JVM参数,-XX:+UseParNewGC和XX:+UseConcMarkSweepGC,其中前者表示对新生代执行并行的垃圾回收机制,而后者表示对老生代执行并行标记-清除垃圾回收机制。可见,JVM允许针对不同内存区执行不同的GC策略。

新生代GC策略 – Parallel New Collector

根据上文所述,对象初始化之后会被放入Young区,更具体的话应该是Eden区,当Eden区满了之后,会进行一次GC。GC算法会检查所有对象的引用情况,如果某个对象还有被引用,表示该对象存活。检查完成之后,会将这些存活的对象移到S0区,并且回收整个Eden区空间,称为一次Minor GC;接着新对象进来,又会放入Eden区,满了之后会检查S0和Eden区存活的对象,将所有存活的对象移到S1区,再回收整个S0和Eden区空间;很容易理解,S0和S1两个区总会有一个区是预留给下次存放存活对象用的。

整个过程可以使用如下图示:

0R9155043-1.png

这种算法称为复制算法,对于这种算法,有两点需要关注:

1. 算法会执行’stop-the-world’暂停,但时间非常短。因为Young区通常会设置的比较小(一般不建议不超过512M),而且JVM会启动大量线程并发执行,一次Minor GC一般都会在几毫秒内完成

2. 不会产生碎片,每次GC之后都会将存活的对象放入连续的空间(S0或S1)

内存中所有对象都会维护一个计数器,每次Minor GC移动一个对象之后,都会为这个对象的计数器加一。当计数器增加到一定阈值之后,算法就会认为该对象生命周期很长,会将其移入老生代。该阈值可以通过JVM参数XX:MaxTenuringThreshold指定。

老生代GC策略 – Concurrent Mark-Sweep

每次执行Minor GC之后,都会有部分生命周期较长的对象被移入老生代,一段时间之后,老生代空间也会被占满。此时就需要针对老生代空间执行GC操作,此处我们介绍Concurrent Mark-Sweep(CMS)算法。CMS算法整个流程分为6个阶段,其中部分阶段会执行 ‘stop-the-world’ 暂停,部分阶段会和应用线程一起并发执行:

1. initial-mark:这个阶段虚拟机会暂停所有正在执行的任务。这一过程虚拟机会标记所有 ‘根对象’,所谓‘根对象’,一般是指一个运行线程直接引用到的对象。虽然会暂停整个JVM,但因为’根对象’相对较少,这个过程通常很快。

2. concurrent mark:垃圾回收器会从‘根节点’开始,将所有引用到的对象都打上标记。这个阶段应用程序的线程和标记线程并发执行,因此用户并不会感到停顿。

3. concurrent precleaning:并发预清理阶段仍然是并发的。在这个阶段,虚拟机查找在执行mark阶段新进入老年代的对象(可能会有一些对象从新生代晋升到老年代, 或者有一些对象被分配到老年代)。

4. remark:在阶段3的基础上对查找到的对象进行重新标记,这一阶段会暂停整个JVM,但是因为阶段3已经欲检查出了所有新进入的对象,因此这个过程也会很快。

5. concurrent sweep:上述3阶段完成了引用对象的标记,此阶段会将所有没有标记的对象作为垃圾回收掉。这个阶段应用程序的线程和标记线程并发执行。

6. concurrent reset:重置CMS收集器的数据结构,等待下一次垃圾回收。

相应的,对于CMS算法,也需要关注两点:

1. ‘stop-the-world’暂停时间也很短暂,耗时较长的标记和清理都是并发执行的。

2. CMS算法在标记清理之后并没有重新压缩分配存活对象,因此整个老生代会产生很多的内存碎片。

CMS Failure Mode

上文提到在正常的情况下CMS整个流程的暂停时间都是很短的,一般也就在10ms~100ms左右。然而这与线上的情况并不相符,线上集群在读写压力很大的情况下,经常会出现长时间的卡顿,有些卡顿甚至长达几分钟,导致很严重的读写阻塞,甚至会造成Region Server和Zookeeper之间Session超时,使得Region Server异常离线。实际上,CMS并不是很完美,它会在两种场景下产生严重的Full GC,接下来分别进行介绍。

Concurrent Failure

这种场景其实比较简单,假如现在系统正在执行CMS回收老生代空间,在回收的过程中新生代来了一批对象进来,不巧的是,老生代已经没有空间再容纳这些对象了。这种场景下,CMS回收器会停止继续工作,系统进入 ’stop-the-world’ 模式,并且回收算法会退化为单线程复制算法,重新分配整个堆内存的存活对象到S0中,释放所有其他空间。很显然,整个过程会非常’漫长’。但是这种问题也很容易解决,只需要让CMS回收器更早一点回收就可以避免。JVM提供了参数-XX:CMSInitiatingOccupancyFraction=N来设置CMS回收的时机,其中N表示当前老生代已使用内存占新生代总内存的比例,该值默认为68,可以将该值修改的更小使得回收更早进行。

Promotion Failure

假设此时设置XX:CMSInitiatingOccupancyFraction=60,但是在已使用内存还没有达到总内存60%的时候,已经没有空间容纳从新生代迁移的对象了。oh,my god!怎么会这样?罪魁祸首就是内存碎片,上文中提到CMS算法会产生大量碎片,当碎片容量积累到一定大小之后就会造成上面的场景。这种场景下,CMS回收器一样会停止工作,进入漫长的 ’stop-the-world’ 模式。JVM也提供了参数 -XX: UseCMSCompactAtFullCollection来减少碎片的产生,这个参数表示会在每次CMS回收垃圾之后执行一次碎片整理,很显然,这个参数会对性能有比较大的影响,对HBase这种对延迟敏感的业务来说并不是一个完美解决方案。

HBase内存碎片统计实验

在实际线上环境中,很少出现Concurrent Failure模式的Full GC,大多数Full GC场景都是Promotion Failure。我们线上集群也会每隔半个月左右就会因为Promotion Failure触发一次Full GC。为了更好地理解CMS策略下内存碎片是如何触发Promotion Failure,接下来我们做一个简单的实验:JVM提供了参数 -XX:PrintFLSStatistics=1来打印每次GC前后内存碎片的统计信息,统计信息主要包括3个维度:Free

Space、Max Chunk Size和Num Chunks,其中Free Space表示老生代当前空闲的总内存容量,Max Chunk Size表示老生代中最大的内存碎片所占的内存容量大小,Num Chunks表示老生代中总的内存碎片数。我们在测试环境集群(共4台Region Server)将这个参数设置为1,然后使用一个客户端YCSB执行Read-And-Write操作,分别统计日志中Free Space和Max Chunk Size两个指标随时间的变化情况。

测试结果如下图所示,其中第一张图表示Total Free Space随时间的变化曲线图,第二张图表示Max Chunk Size随时间变化曲线图。其中横坐标表示时间,纵坐标表示相应内存大小。

0R91535P-2.png

0R915O96-3.png

根据第一张曲线图可知,老生代总的空闲内存容量维持在300M~400M之间,当内存容量到达300M左右时就会进行一次GC,GC后内存容量就会又回到400M左右。而第二张曲线图会更加形象地说明内存碎片导致的Promotion Failure,刚开始随着数据不断写入,Max Chunk Size会不断变小,之后很长一段时间基本维持在30M左右。在横坐标为1093那点,人为地将写入的单条数据大小由500Byte变为5M大小,此后Max Chunk Size会再次减小,当减小到一定程度之后曲线会忽然升高到350M左右,经过日志确认,此时JVM发生了Promotion

Failure模式的Full GC,持续时间约4.91s。此后一段时间Full GC还在持续发生。

经过上述分析,可以知道:CMS GC会不断产生内存碎片,当碎片小到一定程度之后就会基本维持不变,如果此时业务写入一些单条数据量很大的KeyValue,就有可能触发Promotion Failure模式Full GC。

总结

本文首先介绍了两种常见的Java GC策略,再接着介绍了CMS策略可能引起两种模式的Full GC,最后通过一个小实验说明了CMS GC确实产生了内存碎片,而且会导致长时间的Full GC发生。接下来《演进篇》会详细介绍从一开始HBase是如何针对CMS进行优化处理的,敬请期待!

Categories:更多技术交流,请关注我们进行交流与咨询哦!

本条技术文章来源于互联网,如果无意侵犯您的权益请点击此处反馈版权投诉

本文系统来源:php中文网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367640.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CapeDwarf – Java EE上的Google App Engine

我有很多爱好。 从早期的Java EE规范一路走来,并通过Java EE 7进行了“云”之旅,我很好奇看到新宣布的CapeDwarf项目有哪些库存,可以在内部引入Google的平台即服务,提供“ Google App Engine ” 。 到目前为止的故事 我确实使用了…

windows 服务中托管asp.net core

在windows 服务中托管asp.net core SDK 2.1.300 官方示例 1、添加运行标识符xml <PropertyGroup> <TargetFramework>netcoreapp2.1</TargetFramework> <RuntimeIdentifier>win7-x64</RuntimeIdentifier> </PropertyGroup> 2、添加包引用 d…

Bootstrap插件之-按钮插件

按钮 button.js 按钮的功能很丰富。通过控制按钮的状态或创建一组按钮并形成一些新的组件&#xff0c;例如工具条。 跨浏览器兼容性 在页面多次加载之间&#xff0c;Firefox 仍然保持表单控件的状态&#xff08;禁用状态和选择状态&#xff09;。一个解决办法是设置autocomplet…

第二周读书笔记——《构建之法》

【对一些实例的看法】 “我写了二叉树的遍历算法实现&#xff0c;在这里&#xff0c;二叉树是数据结构&#xff0c;遍历的实现细节是算法。C程序就是结果。但是这个程序有什么实际用处呢&#xff1f;在Java和其他一些语言中&#xff0c;似乎没有指针&#xff0c;那我可以不必了…

java springmvc 数据库事务_事务的简单回顾_JavaEE框架(Maven+SpringMvc+Spring+MyBatis)全程实战教程_Java视频-51CTO学院...

SpringMVCSpring MVC属于SpringFrameWork的后续产品&#xff0c;已经融合在Spring Web Flow里面。Spring MVC 分离了控制器、模型对象、分派器以及处理程序对象的角色&#xff0c;这种分离让它们更容易进行定制。SpringSpring是一个开源框架&#xff0c;Spring是于2003 年兴起的…

异步重试模式

当您有一段经常失败且必须重试的代码时&#xff0c;此Java 7/8库提供了丰富且不引人注目的API&#xff0c;并提供了针对此问题的快速且可扩展的解决方案&#xff1a; ScheduledExecutorService scheduler Executors.newSingleThreadScheduledExecutor(); RetryExecutor execu…

前端 ---jQuery的补充

15-jQuery补充 jquery内容补充 jquery除了咱们上面讲解的常用知识点之外&#xff0c;还有jquery 插件、jqueryUI知识点 jqueryUI 官网&#xff1a; https://jqueryui.com/ jqueryUI 中文网&#xff1a; http://www.jqueryui.org.cn/ jquery插件内容包含 官网demo&#xff1a; h…

[软件工程]自我介绍----一个小菜鸡的自我介绍(C++版 手动偷笑~)

#include “iostream” using namespace std; class Lee : protected MyMother,protected MyFather { private:string ChineseName("李怡龙");string EnglishName("Lee");int age 20;string university("青海大学计算机系");string level("…

用Java编写Hadoop MapReduce任务

尽管Hadoop框架本身是使用Java创建的&#xff0c;但MapReduce作业可以用许多不同的语言编写。 在本文中&#xff0c;我将展示如何像其他Java项目一样&#xff0c;基于Maven项目在Java中创建MapReduce作业。 准备示例输入 让我们从一个虚构的商业案例开始。 在这种情况下&#…

java 大二学期总结报告_大二学生自我总结「」

大二学生导师工作总结转眼间&#xff0c;我们的大二学年就这样结束了&#xff0c;在迎接新的一学期前我们来写一份自我总结吧。下面是小编搜集整理的大二学生自我总结&#xff0c;欢迎阅读。更多资讯尽在自我总结栏目!大二学生自我总结回顾大学二年,通过良师的教导和自身的刻苦…

windows下揪出java程序占用cpu很高的线程

背景 天天搞java&#xff0c;这些监控也都知道&#xff0c;用过&#xff0c;但也没往细里追究。因为也没碰见这种问题&#xff0c;这次还是静下来走一遍流程吧。与网上基本一致&#xff0c;不过我区分了下linux和windows的不一样。我感觉基本是程序写成死循环了或者大对象分配多…

jquery -input事件

input输入框的change事件&#xff0c;要在input失去焦点的时候才会触发 $(input[namemyInput]).change(function() { ... }); 在输入框内容变化的时候不会触发change&#xff0c;当鼠标在其他地方点一下才会触发用下面的方法会生效&#xff0c;input [html] view plain copy$(&…

Bootstrap中的下拉列表

下拉列表&#xff08;select&#xff09;注意&#xff0c;很多原生选择菜单单 - 即在 Safari 和 Chrome 中 - 的圆角是无法通过修改 border-radius 属性来改变的。复制<select class"form-control"><option>1</option><option>2</option&…

Spring集成:轻量级集成方法

当今的应用程序希望能够访问企业环境中的所有业务&#xff0c;而无需考虑与绝望的系统无缝集成的应用程序技术。 可以通过使用中间件技术对各种系统进行布线来实现这种集成。 集成平台使应用程序可以相互共享信息的环境&#xff0c;从而使体系结构具有高度的互操作性。 Spring…

动态REM

什么是rem&#xff1f; rem是相对于根元素html字体大小来计算的&#xff0c;即( 1rem html字体大小 ) rem和em区别&#xff1f; rem:&#xff08;root em&#xff0c;根em&#xff09;根元素的fort-size的大小计算em&#xff1a;相对长度单位&#xff0c;相对于当前对象内文本…

java教学楼的属性_java设计一个父类建筑物building,由它派生出教学楼类classroom,然后采用一些数据进行测试....

public class Building {public String bname;//建筑物名称public int floors;//代表总层数public double area;//代表总面积public Building(){}public Building(String bname, int floors, double area) {this.bname bname;this.floors floors;this.area area;}}public cl…

Bootstrap中的列表的使用

列表无序列表排列顺序无关紧要的一列元素。Lorem ipsum dolor sit ametConsectetur adipiscing elitInteger molestie lorem at massaFacilisis in pretium nisl aliquetNulla volutpat aliquam velitPhasellus iaculis nequePurus sodales ultriciesVestibulum laoreet portti…

Vue项目中使用HighChart

记&#xff1a;初次在Vue项目中使用 HighChart 的时候要走的坑 感谢这位哥们的思路 传送门 Vue-cli项目使用 npm install highcharts --save 让我们看看 highcharts 的使用方法&#xff0c;传送门 Highcharts.chart(targetTag, option) 重启项目&#xff0c;建立chart.vue文件 …

form字体和颜色java安卓开发_Android 修改App中默认TextView的字体和颜色

一、别人怎么做来源http://stackoverflow.com/questions/3078081/setting-global-styles-for-views-in-androidActually, you can set a default style for TextViews (and most other built-in widgets) without needing to do a custom java class or setting the style indi…

使用Dozer框架进行Bean操作

介绍 如您所知&#xff0c;您可以在任何操作系统中将文件或文件夹从源位置复制到目标位置。 您是否考虑过复制基本上是POJO的java对象&#xff1f; 在许多情况下&#xff0c;您需要将源bean的内容复制到目标bean。 我不关心对象的拷贝构造函数&#xff0c;浅拷贝或深拷贝或克隆…