一个程序猿面对技术内卷和年龄的拷问-个人分享

Posted erainm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个程序猿面对技术内卷和年龄的拷问-个人分享相关的知识,希望对你有一定的参考价值。

此文章仅代表个人简介,根据个人亲身经历感受所得,希望给更多同行参考,入行有风险,且行且珍惜,哈哈哈,开个玩笑。

很多人都知道我们这个行业近些年都比较赚钱,工资相对较高,我只能说你们并没有看见我们的付出、努力和艰辛,各行各业都是这样,我相信。

作为一个多年经验的资深程序猿,自14年实习开始就进入该行业,从一名程序小白开始,最早做java开发,一步一步,做到中级,再到高级,终于有一天,我发现整天搞这些一点意思都没有,每天还得不断对自己进行技术更新,学习、底层原理、源码剖析等等,窝内个天呀,快结束我吧,没意思、太没意思了;就这样我自己想着转变,刚好大环境下,大数据逐渐成熟,国内需求越来越多,往大数据方向靠靠吧(PS:其实工资诱人 ),就这样我就考虑,我是自学?从公司转型(遗憾,当时公司没有大数据需求)?找个培训班学习?在自己万般纠结中,我毅然决然的选择了裸辞去找家培训机构学习,我现在想来,我也挺佩服我自己的,不过还好,我有个对我从来都是无条件支持的伟大母亲,她信我。这也算是我的一大动力吧,在此,借助这篇文章,默默的对我的母亲表达感谢,直到如今,她都一直对我的决定非常的支持。就这样,我努力学习完六个月,出去找工作,确实,很顺利,薪资也大幅度增长,这些年做大数据,同样我遇到很多问题,各种瓶劲。我不知道为什么,我有一次陷入了盲目,不知道方向在哪,大数据技术很多,我不停的学,不停的报班,不停的总结,不停的看各种书籍、博客、公众号,我突然感觉我好无知,我简直是个垃圾,我不知道该怎么办?我连总结自己的勇气都没有了,我明明很努力了,可为什么还是会这样?

终于我忍不住和我母亲沟通交流,母亲是位教师,他很懂我,也很会劝导我(细节就不细说了,道理大家都懂),终于我决定重新认识自己,对我自己的经历做出总结。

  1. 我觉得我虽然是在做大数据,但是都太过于浅显,并没有好的经历,好的项目,总结起来真正的经验不足。
  2. 认知不够,因为项目局限性,很多公司都是简单浅显做一些基础数仓,简单计算一些指标就可以了,所以自己认知停留,在大数据体系上面认知不够,比如:怎么做一个全域数仓?怎么构建指标体系?怎么做数据治理?怎么做数据管理?怎么做数据平台?怎么做数据中台?等等。。
  3. 很多时候,需要停下脚步,巩固复习,贪多嚼不烂,找准一个方向,一个内容抓实学,必有成长。

就在这个时候,说来也很巧合,抓住自己的弱势,好的项目经验不足,不能充分体现大数据场景以及优势和作用。我被一个培训的大数据内容所吸引(我自己是做大数据的,仿佛自己被大数据杀熟了 ),同时我也第一次感觉大数据杀熟也挺不错的。为什么呢?这个其实就是拉钩教育大数据高薪训练营,我为什么着重说下这个呢,其实真正吸引我的是他们的几个项目,循序渐进,而且做到了真正的大项目,也非常全面。

  1. 真正达到了PB级,这个是我们在做很多大数据项目中遇不到的,这也是我现在的窘迫,从来没有遇到过那个公司哪个项目达到PB级别,没有真正的大数据,很多场景、问题是遇不到的,解决问题的局限性就来了。

  2. 项目全面,我仔细看了项目简介,而且我当时报班时也很犹豫害怕,怕项目深度不够,专门还咨询了授课讲师,期间我们也交流了很多,中肯的建议很受用。就拿第一个离线数仓来说,数量级别达到PB级,而且满足现在市场定位,很全面,不简简单单就只是数仓相关的内容,除了指标体系、数据建模、数据计算等之外的还有埋点数据采集、元数据管理、数据质量监控、即席查询等,这个是很多培训机构做不到的。我真的很喜欢这个。话不多少,没图说个**

  3. 项目种类多,而且很多企业都遇不到的,智慧物流大数据项目、企业级电商实时数仓项目、人才职位画像匹配推荐。项目种类很全,而且循序渐进的进行,保证了质量

看到了没,做了这么多年大数据,我真的没实际做过这样好的大型项目(惭愧惭愧),就像和别人说的,我只是缺乏一个机会,前提是你要时刻做好准备,可以有胜任这个机会的能力。

我通过多方咨询、考察、调研,终于做下决定,一个字,“干”就完了;其实我报了很多学习班,真的,因为我总感觉我欠缺太多,我需要不断补充自己,现在想想,其实不用,找好一个学习机会,扎实去学,调研清楚就行,所以通过我个人推荐,这个高薪训练营真的很不错。为啥?你肯定觉得我是卧底,I swear! 我不是,我很中肯的评价,这个为什么好:

  1. 学习循序渐进,课时是解锁模式,没个课时分为很多节,中间又分了小结作业,用于自测,然后,课时完成会有课时作业,提交后,老师批改。学完一个课时才会解锁下一个课时。我觉得这样很有必要,贪多嚼不烂,就像我上面说的,一步一步的走,每个步骤夯实自己,楼才能盖的更高,虽然前面基础很多我都会,但是我还是按耐着自己的性子,一点点学习,我觉得查缺补漏真的很好,很多以前没注意或者不知道的都被填补了。
  2. 资料齐全,我觉得这个很重要,有时候我们自己搭建什么集群啥的,有点经验完全自己可以胜任,搭建一次能知道很多为什么,资料写的也很全面,这么有什么问题,不这样做又会遇到什么问题,可以解决很多实际问题。
  3. 每个课时自己学完,会有直播的方式,老师带着大家一起温故知新,并且拓展知识面。
  4. 市面上使用的技术栈,训练营都会有讲解,而且非常详细,最关键的是关联项目,有实践的经验。
  5. 笔记,这个真的是,不用说,很重要,像我们大数据技术栈这么多,学习的内容实在是无法用语言表达(高薪-存在即合理),每次提交课时作业时,都会附上自己的学习笔记链接,讲师会核查纠正。
  6. 大厂内推的机会,国内屈指可数的一流招聘网站,给你开玩笑呢?为啥要进大厂,还不是工资高?其实也不全是,在我看来,更多的是因为大厂有实力和机会做大项目,对于我们研发人员来说成长很大。
    说实话,我挺感谢有这样的经历,让我知道我需要什么,我的目标是什么。也祝愿大家可以找到自己满意的工作。
  7. 讲师基本都是大厂出来的,很多都是大厂在职的,身经百战,经验丰富。说不定看中你,直接给你拉进大厂了,哈哈哈
  8. 还有很多辅助你学习的老师、班主任等,除了学习,其它需要帮助的都随时帮助您

因为我才刚学不久(为什么不早点让我遇到这个学习机会 ),附一篇我的学习笔记,大家可以参考:

Hadoop学习笔记

1. MapReduce

Reduce端join缺点:

  • 数据聚合功能在reduce端完成,reduce端并行度一般不高,所以执行效率存在隐患
  • 相同key的数据去往同一个分区,如果数据本身存在不平衡,会造成数据倾斜
    join的操作是在reduce阶段完成,reduce端的处理压力太大,map节点的运算负载 则很低,资源利用率不高,且在reduce阶段极易产生数据倾斜。

解决方案:map端join实现方式

Map端join:

  • 适用于关联表中有小表的情形;
  • 避免了reduce端数据倾斜的问题
    可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果,可以大大提高join操作的并行度,加快处理速度

mr程序中有大量小文件如何处理:

  1. 使用combineInputformat,跳出原先TextInputformat的切片逻辑(一个文件切一个分片,一个分片对应一个mapTask,也就是一个小文件对应一个MapTask),使用combineInputformat,可以将多个小文件合并成一个逻辑分片,由一个mapTask来完成,可以解决mapreduce中一个小文件对应一个mapTask造成资源浪费的问题,这个只是解决在mapreduce层面的问题。但是,这些数据存储到HDFS上,对于NameNode来说,内存依然存在浪费。
  2. 自定义InputFormat合并成一个大文件,可以解决NameNode内存浪费的情况。

MR算法之MergeSort归并排序:

合并


合并细节(降低空间复杂度)

  1. 不断地将当前序列平均分割成 2个子序列 直到不能再分割(序列中只剩 1个元素)
  2. 不断地将 2个子序列合并成一个有序序列 直到最终只剩下 1个子序列

时间复杂度:O(nlogn)
空间复杂度:O(n)

QuickSort - 快排

  • 第一步
    从数组中选择一个轴点元素(Pivot element),一般选择0位置元素为轴点元素

  • 第二步

    • 利用Pivot将数组分割成2个子序列
    • 将小于 Pivot的元素放在Pivot前面(左侧) 、 将大于 Pivot的元素放在Pivot后面(右侧) 、 等于Pivot的元素放哪边都可以(暂定放在左边)
  • 第三步

    • 对子数组进行第一步,第二步操作,直到不能再分割(子数组中只有一个元素)

  • 时间复杂度

    • 最坏情况:
    • 最好情况:
  • 空间复杂度

    • 由于递归调用,每次类似折半效果所以空间复杂度是O(logn)

2. Yarn资源调度

Yarn架构

ResourceManager(rm):处理客户端请求、启动/监控ApplicationMaster、监控NodeManager、资源分配与调度;

NodeManager(nm):单个节点上的资源管理、处理来自ResourceManager的命令、处理来自 ApplicationMaster的命令;

ApplicationMaster(am):数据切分、为应用程序申请资源,并分配给内部任务、任务监控与容错。 Container:对任务运行环境的抽象,封装了CPU、内存等多维资源以及环境变量、启动命令等任务运行相关的信息。

Yarn任务提交(工作机制)

3. Yarn调度策略

FIFO Scheduler

先进先出调度器,按照任务到达时间排序,执行任务

Capacity Scheduler

Apache Hadoop默认调度器
允许多个组织共享整个集群,每个组织获得集群部分计算能力。通过为每个组织分配专门的队列,然后再为每个队列分配一定的集群资源,这样整个集群就可以通过设置多个队列的方式给多个组织提供服务。队列内部又可以垂直划分,这样一个组织内部的多个成员就可以共享这个队列资源,在一个队列内部资源的调度是采用FIFO策略。

Fair Scheduler

公平调度器,CHD版本的Hadoop默认使用的调度器
设计目标是为所有应用分配公平的资源(公平的定义可通过参数设置),公平调度也可以在多个队列间工作。

举个🌰:

假设有两个用户A和B,他们分别拥有一个队列。 当A启动一个job而B没有任务时,A会获得全部集群资源;当B启动一个job后,A的job会继续运 行,不过一会儿之后两个任务会各自获得一半的集群资源。如果此时B再启动第二个job并且其它job还在运行,则它将会和B的第一个job共享B这个队列的资源,也就是B的两个job会用于四分之一的集群资源,而A的job仍然用于集群一半的资源,结果就是资源最终在两个用户之间平等的共享。

4. Yarn多租户资源隔离配置

Yarn集群资源设置为A,B两个队列,

  • A队列设置占用资源70%主要用来运行常规的定时任务,
  • B队列设置占用资源30%主要运行临时任务,
  • 两个队列间可相互资源共享,假如A队列资源占满,B队列资源比较充裕,A队列可以使用B队列的资源,使总体做到资源利用最大化.
    选择使用Fair Scheduler调度策略!!

具体配置:
1、 yarn-site.xml

<!-- 指定我们的任务调度使用fairScheduler的调度方式 -->
<property>
    <name>yarn.resourcemanager.scheduler.class</name>
    <value>org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler</value>
    <description>In case you do not want to use the default scheduler</description>
</property>

2、创建fair-scheduler.xml文件

在Hadoop安装目录下的/etc/hadoop创建

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<allocations>
    <defaultQueueSchedulingPolicy>fair</defaultQueueSchedulingPolicy>
    <queue name="root" >
        <queue name="default">
            <aclAdministerApps>*</aclAdministerApps>
            <aclSubmitApps>*</aclSubmitApps>
            <maxResources>9216 mb,4 vcores</maxResources>
            <maxRunningApps>100</maxRunningApps>
            <minResources>1024 mb,1vcores</minResources>
            <minSharePreemptionTimeout>1000</minSharePreemptionTimeout>
            <schedulingPolicy>fair</schedulingPolicy>
            <weight>7</weight>
        </queue>

        <queue name="queue1">
            <aclAdministerApps>*</aclAdministerApps>
            <aclSubmitApps>*</aclSubmitApps>
            <maxResources>4096 mb,4vcores</maxResources>
            <maxRunningApps>5</maxRunningApps>
            <minResources>1024 mb, 1vcores</minResources>
            <minSharePreemptionTimeout>1000</minSharePreemptionTimeout>
            <schedulingPolicy>fair</schedulingPolicy>
            <weight>3</weight>
         </queue>
    </queue>

    <queuePlacementPolicy>
        <rule create="false" name="specified"/>
        <rule create="true" name="default"/>
    </queuePlacementPolicy>
</allocations>

重启yarn,从界面可以看到

5. MR调优

5.1 Job执行三原则

原则一 充分利用集群资源

Job运行时,尽量让所有的节点都有任务处理,这样能尽量保证集群资源被充分利用,任务的并发度达到最大。可以通过调整处理的数据量大小,以及调整map和reduce个数来实现。

  • Reduce个数的控制使用“mapreduce.job.reduces”
  • Map个数取决于使用了哪种InputFormat,默认的TextFileInputFormat将根据block的个数来分配map数(一个block一个map)。

原则二 ReduceTask并发调整

努力避免出现以下场景

  • 观察Job如果大多数ReduceTask在第一轮运行完后,剩下很少甚至一个ReduceTask刚开始运行。 这种情况下,这个ReduceTask的执行时间将决定了该job的运行时间。可以考虑将reduce个数减少。
  • 观察Job的执行情况如果是MapTask运行完成后,只有个别节点有ReduceTask在运行。这时候集 群资源没有得到充分利用,需要增加Reduce的并行度以便每个节点都有任务处理。

原则三 Task执行时间要合理

一个job中,每个MapTask或ReduceTask的执行时间只有几秒钟,这就意味着这个job的大部分时间 都消耗在task的调度和进程启停上了,因此可以考虑增加每个task处理的数据大小。建议一个task处理时间为1分钟。

5.2 shuffle调优

Shuffle阶段是MapReduce性能的关键部分,包括了从MapTaskask将中间数据写到磁盘一直到ReduceTask拷贝数据并最终放到Reduce函数的全部过程。这一块Hadoop提供了大量的调优参数。

Map阶段

1. 判断Map内存使用

判断Map分配的内存是否够用,可以查看运行完成的job的Counters中(历史服务器),对应的task是 否发生过多次GC,以及GC时间占总task运行时间之比。通常,GC时间不应超过task运行时间的10%, 即GC time elapsed (ms)/CPU time spent (ms)<10%。

Map需要的内存还需要随着环形缓冲区的调大而对应调整。可以通过如下参数进行调整。

mapreduce.map.memory.mb

Map需要的CPU核数可以通过如下参数调整

mapreduce.map.cpu.vcores

内存默认是1G,CPU默认是1核。

如果集群资源充足建议调整:

mapreduce.map.memory.mb=3G(默认1G) mapreduce.map.cpu.vcores=1(默认也是1)

  • 环形缓冲区: Map方法执行后首先把数据写入环形缓冲区,为什么MR框架选择先写内存而不是直接写磁盘?这 样的目的主要是为了减少磁盘i/o
    • 环形缓冲默认100M(mapreduce.task.io.sort.mb),当到达80% (mapreduce.map.sort.spill.percent)时就会溢写磁盘。
    • 每达到80%都会重写溢写到一个新的文件
      当集群内存资源充足,考虑增大mapreduce.task.io.sort.mb提高溢写的效率,而且会减少中间结 果的文件数量。

建议:

  • 调整mapreduce.task.io.sort.mb=512M。
  • 当文件溢写完后,会对这些文件进行合并,默认每次合并 10(mapreduce.task.io.sort.factor)个溢写的文件,建议调整 mapreduce.task.io.sort.factor=64。这样可以提高合并的并行度,减少合并的次数,降低对磁盘操作的次数。

2. Combiner

在Map阶段,有一个可选过程,将同一个key值的中间结果合并,叫做Combiner。(一般将reduce类
设置为combiner即可) 通过Combine,一般情况下可以显著减少Map输出的中间结果,从而减少shuffle过程的网络带宽占用。

建议: 不影响最终结果的情况下,加上Combiner!!

Copy阶段

  • 对Map的中间结果进行压缩,当数据量大时,会显著减少网络传输的数据量
  • 但是也因为多了压缩和解压,带来了更多的CPU消耗。因此需要做好权衡。当任务属于网络瓶颈类型时,压缩Map中间结果效果明显。
  • 在实际经验中Hadoop的运行的瓶颈一般都是IO而不是CPU,压缩一般可以10倍的减少IO操作

Reduce阶段

1、Reduce资

mapreduce.reduce.memory.mb=5G(默认1G)

mapreduce.reduce.cpu.vcores=1(默认为1)。

2、Copy

ReduceTask在copy的过程中默认使用5(mapreduce.reduce.shuffle.parallelcopies参数控制)个
并行度进行复制数据。

该值在实际服务器上比较小,建议调整为50-100.

3、溢写归并

Copy过来的数据会先放入内存缓冲区中,然后当使用内存达到一定量的时候spill磁盘。这里的缓冲区 大小要比map端的更为灵活,它基于JVM的heap size设置。这个内存大小的控制是通过mapreduce.reduce.shuffle.input.buffer.percent(default 0.7)控制的。

shuffile在reduce内存中的数据最多使用内存量为:0.7 × maxHeap of reduce task,内存到磁盘 merge的启动可以通过mapreduce.reduce.shuffle.merge.percent(default0.66)配置。

copy完成后,reduce进入归并排序阶段,合并因子默认为10(mapreduce.task.io.sort.factor参数 控制),如果map输出很多,则需要合并很多趟,所以可以提高此参数来减少合并次数。

mapreduce.reduce.shuffle.parallelcopies #复制数据的并行度,默认5;建议调整为50-100 mapreduce.task.io.sort.factor #一次合并文件个数,默认10,建议调整为64 mapreduce.reduce.shuffle.input.buffer.percent #在shuffle的复制阶段,分配给Reduce输出 缓冲区占堆内存的百分比,默认0.7

mapreduce.reduce.shuffle.merge.percent #Reduce输出缓冲区的阈值,用于启动合并输出和磁盘 溢写的过程

5.3 Job调优

1、推测执行

集群规模很大时(几百上千台节点的集群),个别机器出现软硬件故障的概率就变大了,并且会因此 延长整个任务的执行时间推测执行通过将一个task分给多台机器跑,取先运行完的那个,会很好的解决 这个问题。对于小集群,可以将这个功能关闭。

建议:

  • 大型集群建议开启,小集群建议关闭!
  • 集群的推测执行都是关闭的。在需要推测执行的作业执行的时候开启

2、Slow Start

MapReduce的AM在申请资源的时候,会一次性申请所有的Map资源,延后申请reduce的资源,这样就能达到先执行完大部分Map再执行Reduce的目的。

mapreduce.job.reduce.slowstart.completedmaps

当多少占比的Map执行完后开始执行Reduce。默认5%的Map跑完后开始起Reduce。 如果想要Map完全结束后执行Reduce调整该值为1

3、小文件优化

  • HDFS:hadoop的存储每个文件都会在NameNode上记录元数据,如果同样大小的文件,文件很 小的话,就会产生很多文件,造成NameNode的压力。
  • MR:Mapreduce中一个map默认处理一个分片或者一个小文件,如果map的启动时间都比数据处理的时间还要长,那么就会造成性能低,而且在map端溢写磁盘的时候每一个map最终会产生 reduce数量个数的中间结果,如果map数量特别多,就会造成临时文件很多,而且在reduce拉取数据的时候增加磁盘的IO。

如何处理小文件?

  • 从源头解决,尽量在HDFS上不存储小文件,也就是数据上传HDFS的时候就合并小文件
  • 通过运行MR程序合并HDFS上已经存在的小文件
  • MR计算的时候可以使用CombineTextInputFormat来降低MapTask并行度

4、数据倾斜

MR是一个并行处理的任务,整个Job花费的时间是作业中所有Task最慢的那个了。 为什么会这样呢?为什么会有的Task快有的Task慢?

  • 数据倾斜,每个Reduce处理的数据量不是同一个级别的,所有数据量少的Task已经跑完了,数据量大的Task则需要更多时间。
  • 有可能就是某些作业所在的NodeManager有问题或者container有问题,导致作业执行缓慢。

那么为什么会产生数据倾斜呢?

数据本身就不平衡,所以在默认的hashpartition时造成分区数据不一致问题;那如何解决数据倾斜的问题呢?

  • 默认的是hash算法进行分区,我们可以尝试自定义分区,修改分区实现逻辑,结合业务特点,使得每个分区数据基本平衡
  • 可以尝试修改分区的键,让其符合hash分区,并且使得最后的分区平衡,比如在key前加随机数n- key。
  • 抽取导致倾斜的key对应的数据单独处理。

如果不是数据倾斜带来的问题,而是节点服务有问题造成某些map和reduce执行缓慢呢?

使用推测执行找个其他的节点重启一样的任务竞争,谁快谁为准。推测执行时以空间换时间的优化。
会带来集群资源的浪费,会给集群增加压力。

5.4 YARN调优

1、NM配置

  • 可用内存
    刨除分配给操作系统、其他服务的内存外,剩余的资源应尽量分配给YARN。
    默认情况下,Map或Reduce container会使用1个虚拟CPU内核和1024MB内存, ApplicationMaster使用1536MB内存。

yarn.nodemanager.resource.memory-mb 默认是8192

  • CPU虚拟核数
    建议将此配置设定在逻辑核数的1.5~2倍之间。如果CPU的计算能力要求不高,可以配置为2倍的逻辑CPU。

    yarn.nodemanager.resource.cpu-vcores
    该节点上YARN可使用的虚拟CPU个数,默认是8。
    目前推荐将该值设值为逻辑CPU核数的1.5~2倍之间
    2、Container启动模式

YARN的NodeManager提供2种Container的启动模式。

默认,YARN为每一个Container启动一个JVM,JVM进程间不能实现资源共享,导致资源本地化的时间开销较大。针对启动时间较长的问题,新增了基于线程资源本地化启动模式,能够有效提升container启动效率。

yarn.nodemanager.container-executor.class

  • 设置为“org.apache.hadoop.yarn.server.nodemanager.DefaultContainerExecutor”,则每次启 动container将会启动一个线程来实现资源本地化。
    该模式下,启动时间较短,但无法做到资源(CPU、内存)隔离。
  • 设置为“org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor” ,则每次启动container都会启动一个JVM进程来实现资源本地化。
    该模式下,启动时间较长,但可以提供较好的资源(CPU、内存)隔离能力。

3、AM调优

运行的一个大任务,map总数达到了上万的规模,任务失败,发现是ApplicationMaster(以下简称AM)反应缓慢,最终超时失败。 失败原因是Task数量变多时,AM管理的对象也线性增长,因此就需要更多的内存来管理。AM默认分配的内存大小是1.5GB。

建议: 任务数量多时增大AM内存

yarn.app.mapreduce.am.resource.mb

5.5 Namenode Full GC

JVM堆内存

  • JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。

  • 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量, Survivor两个区占小容量,默认比例是8:1:1。

  • 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。

    JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
    

    1、对象分代

  • 新生成的对象首先放到年轻代Eden区

  • 当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,

  • Survivor0区满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段 时间内总有一个survivor区为空。

  • 经过多次Minor GC仍然存活的对象移动到老年代。

  • 老年代存储长期存活的对象,占满时会触发Major GC(Full GC),GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
    Minor GC : 清理年轻代

Major GC(Full GC) : 清理老年代,清理整个堆空间,会停止应用所有线程。

2、Jstat

查看当前jvm内存使用以及垃圾回收情况

jstat -gc -t 2876 1s #显示pid是2876的垃圾回收堆的行为统计

结果解释:

#C即Capacity 总容量,U即Used 已使用的容量
S0C: 当前survivor0区容量(kB)。
S1C: 当前survivor1区容量(kB)。
S0U: survivor0区已使用的容量(KB)
S1U: survivor1区已使用的容量(KB)
EC: Eden区的总容量(KB)
EU: 当前Eden区已使用的容量(KB)
OC: Old空间容量(kB)。
OU: Old区已使用的容量(KB)
MC: Metaspace空间容量(KB)
MU: Metacspace使用量(KB)
CCSC: 压缩类空间容量(kB)。
CCSU: 压缩类空间使用(kB)。
YGC: 新生代垃圾回收次数
YGCT: 新生代垃圾回收时间
FGC: 老年代 full GC垃圾回收次数
FGCT: 老年代垃圾回收时间
GCT: 垃圾回收总消耗时间

开启HDFS GC详细日志输出

编辑hadoop-env.sh

export HADOOP_LOG_DIR=/hadoop/logs/

增加JMX配置打印详细GC信息

指定一个日志输出目录;注释掉之前的ops

增加新的打印配置

#JMX配置

export HADOOP_JMX_OPTS="-Dcom.sun.management.jmxremote.authenticate=false - Dcom.sun.management.jmxremote.ssl=false"

export HADOOP_NAMENODE_OPTS="-Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:- INFO,RFAS} -Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender} $HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS $HADOOP_DATANODE_OPTS"

export NAMENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -Xloggc:${HADOOP_LOG_DIR}/logs/hadoop-gc.log \\
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime \\
-server -Xms150g -Xmx150g -Xmn20g  -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 \\
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC  -XX:+CMSParallelRemarkEnabled \\
-XX:+CMSClassUnloadingEnabled  -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseFastAccessorMethods  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \\
-XX:+UseGCLogFileRotation  -XX:GCLogFileSize=20m -XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof \\
"

export DATENODE_OPTS="-verbose:gc -XX:+PrintGCDetails -Xloggc:${HADOOP_LOG_DIR}/hadoop-gc.log \\
-XX:+PrintGCDateStamps -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime \\
-server -Xms15g -Xmx15g -Xmn4g  -XX:SurvivorRatio=8  -XX:MaxTenuringThreshold=15 \\
-XX:ParallelGCThreads=18 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC  -XX:+CMSParallelRemarkEnabled \\
-XX:+CMSClassUnloadingEnabled  -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseFastAccessorMethods  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSMaxAbortablePrecleanTime=5000 \\
-XX:+UseGCLogFileRotation -XX:GCLogFileSize=20m -XX:ErrorFile=${HADOOP_LOG_DIR}/logs/hs_err.log.%p -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=${HADOOP_LOG_DIR}/logs/%p.hprof
\\
"

export HADOOP_NAMENODE_OPTS="$NAMENODE_OPTS $HADOOP_NAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="$DATENODE_OPTS $HADOOP_DATANODE_OPTS"
  • -Xms150g -Xmx150g:堆内存大小最大和最小都是150g
  • -Xmn20g :新生代大小为20g,等于eden+2*survivor,意味着老年代为150-20=130g。
  • -XX:SurvivorRatio=8 :Eden和Survivor的大小比值为8,意味着两个Survivor区和一个Eden区的比值为2:8,一个Survivor占整个年轻代的1/10
  • -XX:ParallelGCThreads=10 :设置ParNew GC的线程并行数,默认为8+(Runtime.availableProcessors-8) * 5/8,24核机器为18.
  • -XX:MaxTenuringThreshold=15 :设置对象在年轻代的最大年龄,超过这个年龄则会晋升到老年代
  • -XX:+UseParNewGC :设置新生代使用Parallel New GC
  • -XX:+UseConcMarkSweepGC :设置老年代使用CMS GC,当此项设置时候自动设置新生代为ParNew GC
  • -XX:CMSInitiatingOccupancyFraction=70 : 老年代第一次占用达到该百分比时候,就会引发CMS的第一次垃圾回收周期。后继CMS GC由 HotSpot自动优化计算得到。

3、GC 日志解析

jstat命令输出

查看GC日志输出

  1. ParNew: 16844397K->85085K(18874368K), 0.0960456 secs

其中, 16844397K 表示GC前的新生代占用量, 85085K 表示GC后的新生代占用量,GC后Eden和 一个Survivor为空,所以 85085K 也是另一个Survivor的占用量。括号中的 18874368K 是Eden+一 个被占用Survivor的总和(18g)。

  1. 116885867K->100127390K(155189248K), 0.0961542 secs
    其中,分别是Java堆在垃圾回收前后的大小,和Java堆大小。说明堆使用为 116885867K=111.47g,回收大小为100127390K=95.49g,堆大小为155189248K=148g(去掉 其中一个Survivor),回收了16g空间.

总结:

在HDFS Namenode内存中的对象大都是文件,目录和blocks,这些数据只要不被程序或者数据的拥 有者人为的删除,就会在Namenode的运 行生命期内一直存在,所以这些对象通常是存在在old区中, 所以,如果整个hdfs文件和目录数多,blocks数也多,内存数据也会很大,如何降低Full GC的影响?

  • 计算NN所需的内存大小,合理配置JVM
  • 使用低卡顿G1收集器
    为什么会有G1呢?

因为并发、并行和CMS垃圾收集器都有2个共同的问题:

  • 老年代收集器大部分操作都必须扫描整个老年代空间(标记,清除和压缩)。这就导致了GC随着Java堆空间而线性增加或减少
  • 年轻代和老年代是独立的连续内存块,所以要先决定年轻代和年老代放在虚拟地址空间的位置

G1垃圾收集器利用分而治之的思想将堆进行分区,划分为一个个的区域。

G1垃圾收集器将堆拆成一系列的分区,这样的话,大部分的垃圾收集操作就只在一个分区内执行,从而避免很多GC操作在整个Java堆或者整个年轻代进行。

编辑hadoop-env.sh

export HADOOP_NAMENODE_OPTS="-server -Xmx220G -Xms200G -XX:+UseG1GC -XX:MaxGCPauseMillis=200  -XX:+UnlockExperimentalVMOptions -XX:+ParallelRefProcEnabled -XX:-ResizePLAB -XX:+PerfDisableSharedMem -XX:-OmitStackTraceInFastThrow -XX:G1NewSizePercent=2 -XX:ParallelGCThreads=23 -XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=32M -XX:G1HeapWastePercent=10 -XX:G1MixedGCCountTarget=16 -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=100M -Xloggc:/var/log/hbase/gc.log -Dhadoop.security.logger=${HADOOP_SECURITY_LOGGER:-
INFO,RFAS} -Dhdfs.audit.logger=${HDFS_AUDIT_LOGGER:-INFO,NullAppender}
$HADOOP_NAMENODE_OPTS"

注意:如果现在采用的垃圾收集器没有问题,就不要选择G1,如果追求低停顿,可以尝试使用G1

这还只是针对我个人写的笔记,是不是超级详细,也有很多你没接触到的?

技术没有上限,学习永不止步,同样年龄不是问题!
大家加油,一起努力

以上是关于一个程序猿面对技术内卷和年龄的拷问-个人分享的主要内容,如果未能解决你的问题,请参考以下文章

程序员行业大洗牌?!

程序员行业大洗牌?!

内卷和焦虑

面对程序员严重的内卷,应该先提升学历,还是先提升技术?

被迫“内卷”的程序员,前途真的一片灰暗吗?

Java面试系列之并发编程专题-Synchronized灵魂拷问