国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

揭露 FileSystem 引起的線上 JVM 內(nèi)存溢出問題

開發(fā)
本文主要介紹了由FileSystem類引起的一次線上內(nèi)存泄漏導(dǎo)致內(nèi)存溢出的問題分析解決全過程。

內(nèi)存泄漏定義(memory leak):一個不再被程序使用的對象或變量還在內(nèi)存中占有存儲空間,JVM不能正常回收改對象或者變量。一次內(nèi)存泄漏似乎不會有大的影響,但內(nèi)存泄漏堆積后的后果就是內(nèi)存溢出。

內(nèi)存溢出(out of memory):是指在程序運行過程中,由于分配的內(nèi)存空間不足或使用不當(dāng)?shù)仍颍瑢?dǎo)致程序無法繼續(xù)執(zhí)行的一種錯誤,此時就會報錯OOM,即所謂的內(nèi)存溢出。 

一、背景

周末小葉正在王者峽谷亂殺,手機(jī)突然收到大量機(jī)器CPU告警,CPU使用率超過80%就會告警,同時也收到該服務(wù)的Full GC告警。該服務(wù)是小葉項目組非常重要的服務(wù),小葉趕緊放下手中的王者榮耀打開電腦查看問題。

圖片

圖片

圖1.1 CPU告警   Full GC告警

二、問題發(fā)現(xiàn)

2.1 監(jiān)控查看

因為服務(wù)CPU和Full GC告警了,打開服務(wù)監(jiān)控查看CPU監(jiān)控和Full GC監(jiān)控,可以看到兩個監(jiān)控在同一時間點都有一個異常凸起,可以看到在CPU告警的時候,Full GC特別頻繁,猜測可能是Full GC導(dǎo)致的CPU使用率上升告警。

圖片

圖2.1 CPU使用率

圖片

圖2.2 Full GC次數(shù)  

2.2 內(nèi)存泄漏

從Full Gc頻繁可以知道服務(wù)的內(nèi)存回收肯定存在問題,故查看服務(wù)的堆內(nèi)存、老年代內(nèi)存、年輕代內(nèi)存的監(jiān)控,從老年代的常駐內(nèi)存圖可以看到,老年代的常駐內(nèi)存越來越多,老年代對象無法回收,最后常駐內(nèi)存全部被占滿,可以看出明顯的內(nèi)存泄漏。

圖片

圖2.3 老年代內(nèi)存

圖片

圖2.4 JVM內(nèi)存


2.3 內(nèi)存溢出

從線上的錯誤日志也可以明確知道服務(wù)最后是OOM了,所以問題的根本原因是內(nèi)存泄漏導(dǎo)致內(nèi)存溢出OOM,最后導(dǎo)致服務(wù)不可用

圖片


圖2.5 OOM日志    

三、問題排查

3.1 堆內(nèi)存分析

在明確問題原因為內(nèi)存泄漏之后,我們第一時間就是dump服務(wù)內(nèi)存快照,將dump文件導(dǎo)入至MAT(Eclipse Memory Analyzer)進(jìn)行分析。Leak Suspects 進(jìn)入疑似泄露點視圖。

圖片

圖3.1 內(nèi)存對象分析

圖片


圖3.2 對象鏈路圖

打開的dump文件如圖3.1所示,2.3G的堆內(nèi)存   其中 org.apache.hadoop.conf.Configuration對象占了1.8G,占了整個堆內(nèi)存的78.63%

展開該對象的關(guān)聯(lián)對象和路徑,可以看到主要占用的對象為HashMap,該HashMap由FileSystem.Cache對象持有,再上層就是FileSystem。可以猜想內(nèi)存泄漏大概率跟FileSystem有關(guān)。

3.2 源碼分析

找到內(nèi)存泄漏的對象,那么接下來一步就是找到內(nèi)存泄漏的代碼。

在圖3.3我們的代碼里面可以發(fā)現(xiàn)這么一段代碼,在每次與hdfs交互時,都會與hdfs建立一次連接,并創(chuàng)建一個FileSystem對象。但在使用完FileSystem對象之后并未調(diào)用close()方法釋放連接。

但是此處的Configuration實例和FileSystem實例都是局部變量,在該方法執(zhí)行完成之后,這兩個對象都應(yīng)該是可以被JVM回收的,怎么會導(dǎo)致內(nèi)存泄漏呢?

圖片

圖3.3

(1)猜想一:FileSystem是不是有常量對象?

接下里我們就查看FileSystem類的源碼,FileSystem的init和get方法如下:

圖片

圖片

圖片

圖3.4

從圖3.4最后一行代碼可以看到,F(xiàn)ileSystem類存在一個CACHE,通過disableCacheName控制是否從該緩存拿對象。該參數(shù)默認(rèn)值為false。也就是默認(rèn)情況下會通過CACHE對象返回FileSystem。

圖片

圖3.5

從圖3.5可以看到CACHE為FileSystem類的靜態(tài)對象,也就是說,該CACHE對象會一直存在不會被回收,確實存在常量對象CACHE,猜想一得到驗證。

那接下來看一下CACHE.get方法:

圖片

從這段代碼中可以看出:

  1. 在Cache類內(nèi)部維護(hù)了一個Map,該Map用于緩存已經(jīng)連接好的FileSystem對象,Map的Key為Cache.Key對象。每次都會通過Cache.Key獲取FileSystem,如果未獲取到,才會繼續(xù)創(chuàng)建的流程。
  2. 在Cache類內(nèi)部維護(hù)了一個Set(toAutoClose),該Set用于存放需自動關(guān)閉的連接。在客戶端關(guān)閉時會自動關(guān)閉該集合中的連接。
  3. 每次創(chuàng)建的FileSystem都會以Cache.Key為key,F(xiàn)ileSystem為Value存儲在Cache類中的Map中。那至于在緩存時候是否對于相同hdfs URI是否會存在多次緩存,就需要查看一下Cache.Key的hashCode方法了。

Cache.Key的hashCode方法如下:

圖片

schema和authority變量為String類型,如果在相同的URI情況下,其hashCode是一致。而unique該參數(shù)的值每次都是0。那么Cache.Key的hashCode就由ugi.hashCode()決定。

由以上代碼分析可以梳理得到:

  1. 業(yè)務(wù)代碼與hdfs交互過程中,每次交互都會新建一個FileSystem連接,結(jié)束時并未關(guān)閉FileSystem連接。
  2. FileSystem內(nèi)置了一個static的Cache,該Cache內(nèi)部有一個Map,用于緩存已經(jīng)創(chuàng)建連接的FileSystem。
  3. 參數(shù)fs.hdfs.impl.disable.cache,用于控制FileSystem是否需要緩存,默認(rèn)情況下是false,即緩存。
  4. Cache中的Map,Key為Cache.Key類,該類通過schem,authority,ugi,unique 4個參數(shù)來確定一個Key,如上Cache.Key的hashCode方法。

(2)猜想二:FileSystem同樣hdfs URI是不是多次緩存?

FileSystem.Cache.Key構(gòu)造函數(shù)如下所示:ugi由UserGroupInformation的getCurrentUser()決定。

圖片

繼續(xù)看UserGroupInformation的getCurrentUser()方法,如下:

圖片

其中比較關(guān)鍵的就是是否能通過AccessControlContext獲取到Subject對象。在本例中通過get(final URI uri, final Configuration conf,final String user)獲取時候,在debug調(diào)試時,發(fā)現(xiàn)此處每次都能獲取到一個新的Subject對象。也就是說相同的hdfs路徑每次都會緩存一個FileSystem對象

猜想二得到驗證:同一個hdfs URI會進(jìn)行多次緩存,導(dǎo)致緩存快速膨脹,并且緩存沒有設(shè)置過期時間和淘汰策略,最終導(dǎo)致內(nèi)存溢出。

(3)FileSystem為什么會重復(fù)緩存?

那為什么會每次都獲取到一個新的Subject對象呢,我們接著往下看一下獲取AccessControlContext的代碼,如下:

圖片

其中比較關(guān)鍵的是getStackAccessControlContext方法,該方法調(diào)用了Native方法,如下:

圖片

該方法會返回當(dāng)前堆棧的保護(hù)域權(quán)限的AccessControlContext對象。

我們通過圖3.6 get(final URI uri, final Configuration conf,final String user) 方法可以看到,如下:

  • 先通過UserGroupInformation.getBestUGI方法獲取了一個UserGroupInformation對象。
  • 然后在通過UserGroupInformation的doAs方法去調(diào)用了get(URI uri, Configuration conf)方法
  • 圖3.7 UserGroupInformation.getBestUGI方法的實現(xiàn),此處關(guān)注一下傳入的兩個參數(shù)ticketCachePath,user。ticketCachePath是獲取配置hadoop.security.kerberos.ticket.cache.path的值,在本例中該參數(shù)未配置,因此ticketCachePath為空。user參數(shù)是本例中傳入的用戶名。
  • ticketCachePath為空,user不為空,因此最終會執(zhí)行圖3.7的createRemoteUser方法

圖片

圖3.6

圖片

圖3.7

圖片

圖3.8

從圖3.8標(biāo)紅的代碼可以看到在createRemoteUser方法中,創(chuàng)建了一個新的Subject對象,并通過該對象創(chuàng)建了UserGroupInformation對象。至此,UserGroupInformation.getBestUGI方法執(zhí)行完成。

接下來看一下UserGroupInformation.doAs方法(FileSystem.get(final URI uri, final Configuration conf, final String user)執(zhí)行的最后一個方法),如下:

圖片

然后在調(diào)用Subject.doAs方法,如下:

圖片

最后在調(diào)用AccessController.doPrivileged方法,如下:

圖片

該方法為Native方法,該方法會使用指定的AccessControlContext來執(zhí)行

PrivilegedExceptionAction,也就是調(diào)用該實現(xiàn)的run方法。即FileSystem.get(uri, conf)方法。

至此,就能夠解釋在本例中,通過get(final URI uri, final Configuration conf,final String user) 方法創(chuàng)建FileSystem時,每次存入FileSystem的Cache中的Cache.key的hashCode都不一致的情況了。

小結(jié)一下:

  1. 在通過get(final URI uri, final Configuration conf,final String user)方法創(chuàng)建FileSystem時,由于每次都會創(chuàng)建新的UserGroupInformationSubject對象。
  2.  在Cache.Key對象計算hashCode時,影響計算結(jié)果的是調(diào)用了UserGroupInformation.hashCode方法。
  3. UserGroupInformation.hashCode方法,計算為:System.identityHashCode(subject)。即如果Subject是同一個對象則返回相同的hashCode,由于在本例中每次都不一樣,因此計算的hashCode不一致。
  4. 綜上,就導(dǎo)致每次計算Cache.key的hashCode不一致,便會重復(fù)寫入FileSystem的Cache。

(4)FileSystem的正確用法

從上述分析,既然FileSystem.Cache都沒有起到應(yīng)起的作用,那為什么要設(shè)計這個Cache呢。其實只是我們的用法沒用對而已。

在FileSystem中,有兩個重載的get方法:

public static FileSystem get(final URI uri, final Configuration conf, final String user)
public static FileSystem get(URI uri, Configuration conf)

圖片

我們可以看到 FileSystem get(final URI uri, final Configuration conf, final String user)方法最后是調(diào)用FileSystem get(URI uri, Configuration conf)方法的,區(qū)別在于FileSystem get(URI uri, Configuration conf)方法于缺少也就是缺少每次新建Subject的的操作。

圖片

圖3.9

沒有新建Subject的的操作,那么圖3.9 中Subject為null,會走最后的getLoginUser方法獲取loginUser。而loginUser是靜態(tài)變量,所以一旦該loginUser對象初始化成功,那么后續(xù)會一直使用該對象。UserGroupInformation.hashCode方法將會返回一樣的hashCode值。也就是能成功的使用到緩存在FileSystem的Cache。

圖片

圖片

圖3.10

四、解決方案

經(jīng)過前面的介紹,如果要解決FileSystem 存在的內(nèi)存泄露問題,我們有以下兩種方式:

(1)使用public static FileSystem get(URI uri, Configuration conf):

  • 該方法是能夠使用到FileSystem的Cache的,也就是說對于同一個hdfs URI是只會有一個FileSystem連接對象的。
  • 通過System.setProperty("HADOOP_USER_NAME", "hive")方式設(shè)置訪問用戶。
  • 默認(rèn)情況下fs.automatic.close=true,即所有的連接都會通過ShutdownHook關(guān)閉。

(2)使用public static FileSystem get(final URI uri, final Configuration conf, final String user):

  • 該方法如上分析,會導(dǎo)致FileSystem的Cache失效,且每次都會添加至Cache的Map中,導(dǎo)致不能被回收。
  • 在使用時,一種方案是:保證對于同一個hdfs URI只會存在一個FileSystem連接對象。
  • 另一種方案是:在每次使用完FileSystem之后,調(diào)用close方法,該方法會將Cache中的FileSystem刪除。

圖片

圖片

圖片

基于我們已有的歷史代碼最小改動的前提下,我們選擇了第二種修改方式。在我們每次使用完FileSystem之后都關(guān)閉FileSystem對象。

五、優(yōu)化結(jié)果

對代碼進(jìn)行修復(fù)發(fā)布上線之后,如下圖一所示,可以看到修復(fù)之后老年代的內(nèi)存可以正常回收了,至此問題終于全部解決。

圖片


圖片

六、總結(jié)

內(nèi)存溢出是 Java 開發(fā)中最常見的問題之一,其原因通常是由于內(nèi)存泄漏導(dǎo)致內(nèi)存無法正常回收引起的。在我們這篇文章中,詳細(xì)介紹一次完整的線上內(nèi)存溢出的處理過程。

總結(jié)一下我們在碰到內(nèi)存溢出時候的常用解決思路:

(1)生成堆內(nèi)存文件

在服務(wù)啟動命令添加

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/base

讓服務(wù)在發(fā)生oom時自動dump內(nèi)存文件,或者使用 jamp 命令dump內(nèi)存文件。

(2)堆內(nèi)存分析:使用內(nèi)存分析工具幫助我們更深入地分析內(nèi)存溢出問題,并找到導(dǎo)致內(nèi)存溢出的原因。以下是幾個常用的內(nèi)存分析工具:

  • Eclipse Memory Analyzer:一款開源的 Java 內(nèi)存分析工具,可以幫助我們快速定位內(nèi)存泄漏問題。
  • VisualVM Memory Analyzer:一個基于圖形化界面的工具,可以幫助我們分析java應(yīng)用程序的內(nèi)存使用情況。

(3)根據(jù)堆內(nèi)存分析定位到具體的內(nèi)存泄漏代碼。

(4)修改內(nèi)存泄漏代碼,重新發(fā)布驗證。

內(nèi)存泄漏是內(nèi)存溢出的常見原因,但不是唯一原因。常見導(dǎo)致內(nèi)存溢出問題的原因還是有:超大對象、堆內(nèi)存分配太小、死循環(huán)調(diào)用等等都會導(dǎo)致內(nèi)存溢出問題。

在遇到內(nèi)存溢出問題時,我們需要多方面思考,從不同角度分析問題。通過我們上述提到的方法和工具以及各種監(jiān)控幫助我們快速定位和解決問題,提高我們系統(tǒng)的穩(wěn)定性和可用性。

責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2010-09-26 15:53:25

JVM內(nèi)存溢出

2021-03-06 10:25:19

內(nèi)存Java代碼

2021-02-03 15:12:08

java內(nèi)存溢出

2012-05-15 02:04:22

JVMJava

2010-09-27 11:12:46

MyEclipseJVM內(nèi)存

2023-08-29 11:38:27

Java內(nèi)存

2025-06-16 07:40:00

2018-05-15 08:44:44

TensorFlowKeras內(nèi)存

2022-03-25 09:01:16

CSS溢出屬性

2022-08-05 11:55:13

FlutteriOS

2015-12-28 11:41:57

JVM內(nèi)存區(qū)域內(nèi)存溢出

2024-01-31 10:11:41

Redis內(nèi)存

2021-04-21 07:37:19

JVM復(fù)盤 日志

2011-08-25 10:50:32

SQL Server數(shù)Performance

2023-03-03 12:37:50

JavaJVM內(nèi)存溢出

2025-01-08 08:47:44

Node.js內(nèi)存泄露定時器

2020-08-10 17:49:25

JVM內(nèi)存溢出

2011-11-28 10:50:56

JavaJVM優(yōu)化

2009-07-06 14:35:26

JVM可用內(nèi)存JSP

2024-10-10 15:32:51

點贊
收藏

51CTO技術(shù)棧公眾號

91社区国产高清| 五月天激情图片| 黄色在线观看视频网站| 国产91精品一区二区麻豆亚洲| 色一情一乱一伦一区二区三欧美| 在线视频国产三级| 亚洲精品二区| 亚洲精选av| 欧美一级高清片| 91热这里只有精品| 欧美日韩在线免费| 五月激情在线| 久久午夜电影| 日韩精品视频免费专区在线播放| 亚洲不卡中文字幕无码| 欧美日韩国产色综合一二三四| 中文字幕精品网| 91成人高清| 亚洲国产精品国自产拍av| 国产精品一区二区不卡视频| 免费一区二区三区四区| 精品久久久中文| 国产伦精品一区二区三区视频黑人 | 久久电影网站中文字幕| 蜜月aⅴ免费一区二区三区 | 国产成人免费av一区二区午夜 | 久久久噜噜噜久噜久久| 99riav在线| 国产亚洲1区2区3区| 久久国产一区二区| 亚洲经典视频| 欧美va日韩va| 中文字幕网在线| 国产亚洲欧美一级| 国产精品久久久影院| 精品999成人| 国产精品7m视频| 日韩一二三区在线观看| 精品国产91乱码一区二区三区 | 成人av资源网站| 成人欧美在线视频| 自拍偷拍第1页| 成人激情动漫在线观看| 久久国产精品久久| 亚洲国产成人精品女人| 97婷婷大伊香蕉精品视频| 日日夜夜精品| 亚洲视频欧洲视频| 免费污视频在线观看| 亚洲午夜一区二区| 久热精品在线播放| 国产亚洲精品bt天堂精选| 97在线免费视频观看| 久久婷婷一区| 日本在线观看一区二区| 欧美一区二区三区久久精品| 国产精品福利网站| 天堂一区二区三区四区| 97久久精品在线| 精品欠久久久中文字幕加勒比 | 亚洲高清视频一区| 久久不射网站| 免费观看成人高| 亚洲伦理精品| 狠狠色噜噜狠狠色综合久| 亚洲国产一区二区在线观看 | 日韩视频在线一区二区| 五月婷婷在线观看| 欧美专区日韩专区| 美女黄视频在线观看| 欧美一区二区三区日韩视频| 国产视频在线播放| 精品国产一区二区三区久久久蜜月| 天天干在线视频论坛| 日韩精品中文字幕一区| 国产777精品精品热热热一区二区| 亚洲丁香婷深爱综合| 欧美电影免费观看网站| 最近中文字幕日韩精品 | 9l视频自拍蝌蚪9l视频成人 | 欧美3p在线观看| 97久久精品午夜一区二区| 欧美在线电影| 91在线免费看片| 99riav国产精品| 欧美日韩在线免费观看视频| 丰满亚洲少妇av| 成人福利网址| 午夜欧美巨大性欧美巨大 | 国产精品一区二区免费在线观看| 国产黄人亚洲片| 久久婷婷国产精品| 亚洲美女视频在线| 草莓福利社区在线| 久久精品最新地址| 永久91嫩草亚洲精品人人| 99热一区二区三区| 亚洲综合男人的天堂| 黄色在线免费观看网站| 91高清免费视频| 巨乳诱惑日韩免费av| 91精品免费久久久久久久久| 国产福利免费在线观看| 亚洲第一级黄色片| 日本亚洲最大的色成网站www| caoporn成人| 手机在线看福利| 成人综合国产精品| 日韩中文字幕在线看| 国产女优一区| 国产精品igao视频网网址不卡日韩| 超碰在线免费看| 日本一区免费观看| 国产精品91久久久久久| 国产婷婷97碰碰久久人人蜜臀| 国产精品的网站| 91麻豆高清视频| 韩国三级在线一区| 麻豆视频在线看| 国产福利在线播放| 亚洲视频在线二区| 日韩av电影在线播放| 欧美一区二区视频免费观看| 亚洲狼人综合| 疯狂欧洲av久久成人av电影 | 麻豆视频在线观看免费网站| 国产人成亚洲第一网站在线播放| 国产四区在线观看| 日韩欧美国产wwwww| 亚洲一区二区三区在线| 国产三级精品三级在线专区| 爽爽淫人综合网网站| 国产精品国产成人国产三级| 国产麻豆精品一区二区| 一区二区国产精品| 免费一级欧美在线大片| 精品人人视频| 成av人片在线观看www| www.涩涩涩| 看欧美ab黄色大片视频免费 | 91麻豆精品国产91久久久久| 夜夜精品视频一区二区| 日韩欧美国产wwwww| 色婷婷精品大在线视频 | 国产一区二区三区三区在线观看| 精品国产网站在线观看| 日韩视频免费在线观看| 午夜精品一区二区三区av| 亚洲欧美日韩一区在线| 亚洲精品久久久久中文字幕欢迎你| 亚洲韩国精品一区| 国产精品一二三| 精品一区二区综合| 欧美日日夜夜| 91看片一区| 香蕉成人在线| 欧美美女在线直播| 久久中文视频| 日韩—二三区免费观看av| 亚洲精品3区| 香蕉久久精品| 国产成人福利av| 亚洲综合色婷婷在线观看| 色婷婷精品视频| 91一区二区三区四区| 西西人体一区二区| 国产不卡一区视频| 97se狠狠狠综合亚洲狠狠| 欧美影视一区在线| 在线观看成人免费视频| 欧美日本国产在线| 98精品在线视频| 日韩少妇中文字幕| 欧美mv和日韩mv的网站| 精品综合免费视频观看| 中文字幕在线直播| 丝袜人妻一区二区三区| www欧美日韩| 中文字幕av资源一区| 国产精品一线天粉嫩av| 高潮白浆女日韩av免费看| 日韩综合中文字幕| 国外成人免费在线播放| 日韩亚洲视频| 国产丝袜高跟一区| 成人国产精品免费观看视频| 91成人小视频| 国产视频青青| 狠狠爱一区二区三区| 亚洲国产91色在线| 久久精品夜色噜噜亚洲a∨| 欧美特黄一级大片| 日本免费在线观看| 久久www视频| 97国产精品视频| 欧美午夜精品免费| 国产98色在线|日韩| 精品一区av| 日本在线影院| 国产精品青草综合久久久久99|