成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

JVM堆外內存導致的FGC問題排查

開發 后端
Java虛擬機定義了程序執行期間使用的各種運行時數據區域。其中一些數據區域是在Java虛擬機啟動時創建的,只有在Java虛擬機退出時才會被銷毀,這部分線程共有。其他數據區域為每個線程。每線程數據區域在創建線程時創建,在線程退出時銷毀,也就是線程私有。

問題發現

服務在線上環境頻繁的Full GC。把相關運行時數據區的監控打開,發現堆外內存一直在上升。

圖片

我使用的版本是 java8,jvm廠商是orcale hotspot,垃圾回收器使用的CMS+ParNew。

我使用的jvm參數是:

-Xmx6g
-Xms6g
-XX:NewRatio=1
-XX:+UseConcMarkSweepGC
-XX:CMSInitiatingOccupancyFraction=75
-XX:+UseCMSInitiatingOccupancyOnly
-XX:MaxTenuringThreshold=6
-XX:+ParallelRefProcEnabled
-XX:+CMSParallelRemarkEnabled
-XX:+UseCMSCompactAtFullCollection
-XX:+heapDumpOnOutOfMemoryError
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-Xloggc:/export/Logs/gc.log

為了明確排查方向,需要研究堆外內存都具體有什么東西。于是我翻看了jvm的虛擬機規范。解讀如下:

Java虛擬機運行時數據區

Java虛擬機定義了程序執行期間使用的各種運行時數據區域。其中一些數據區域是在Java虛擬機啟動時創建的,只有在Java虛擬機退出時才會被銷毀,這部分線程共有。其他數據區域為每個線程。每線程數據區域在創建線程時創建,在線程退出時銷毀,也就是線程私有。

運行時數據區分為以下幾個部分:

1、PC寄存器(The pc Register)

每個線程一個,以保存當前執行指令的地址。一旦執行了指令,PC寄存器將用下一條指令更新。

2、虛擬機棧( Java Virtual Machine Stacks)

每個Java虛擬機線程都有一個私有Java虛擬機堆棧,與線程同時創建。虛擬機棧存儲棧幀,它保存局部變量和部分結果。

虛擬機棧可能會出現Java虛擬機將拋出StackOverflowerError。

3、堆(Heap)

Java虛擬機線程之間共享堆,堆只有一個。堆是為所有類實例和數組分配內存的運行時數據區域。這也是我們創建的對象放置的區域。是最大的,最需要調優的地方。

堆是在虛擬機啟動時創建的。對象的堆存儲由垃圾收集器回收;對象永遠不會顯式解除分配。

如果計算需要的堆超過了自動存儲管理系統的可用堆,Java虛擬機會拋出OutOfMemoryError。

4、方法區(Method Area)

存儲所有類級別的數據,包括靜態變量所有線程共享。Java虛擬機只有一個方法區。存儲的有類結構,例如運行時常量池、字段和方法數據,以及方法和構造函數的代碼,包括類和實例初始化以及接口初始化中使用的特殊方法。

5、運行時常量池(Run-Time Constant Pool)

運行時常量池是類文件中常量池表的每類或每接口運行時表示形式。它包含多種常量,從編譯時已知的數字文本到必須在運行時解析的方法和字段引用。運行時常量池的功能類似于傳統編程語言的符號表,盡管它包含比典型符號表更廣泛的數據范圍。

這段我抄的,為了保持完整性,運行時常量池其實是方法區的一部分。

6、本地方法棧(Native Method Stacks)

存儲本地方法信息,線程私有。

整體結構表示如下?


圖片


問題:方法區和元空間有什么關系?

簡單理解,方法區是java的定義,而元空間則是hotspot虛擬機在1.8及其以后的實現。在1.7之前叫永久代(永久代還包含了部分老年對象),如果使用java8的話忽略永久代就行了。

根據jvm的規范,方法區內存儲的都是jvm類級別的數據,包括什么構造方法,什么常量池什么的。那什么操作會使得這方面一直在上漲呢?帶著問題,一步步搞唄。

簡單嘗試

首先先定死metaspce的大小,不讓他動態擴容,因為元空間每次調整大小都會進行一次full gc。

jvm啟動參數新增。

-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=512m

但是發現并沒有用。

是否能從堆看出些端倪?

堆外內存,沒有特別好的查看方法。我決定還是把堆內存dump下來看看,看能否通過堆內存,看出一些貓膩來。

將堆dump下來進行分析。

使用命令 jps 找到java進程pid,指定生成文件的path。

jmap -dump:file=/path ${pid}

dump完畢后。

借助工具進行查詢 首先使用mat,官方網站:https://www.eclipse.org/mat/。

圖片圖片

這邊看到了很多Netty的PoolThreaCache。

聯想到netty使用了直接內存,是否和這個有關呢?

為此查詢了大量資料,找到了一個參數:-Dio.netty.maxDirectMemory 。

這個參數大概意思是調整netty堆外內存,通過它有三個取值,無論調成什么都沒辦法阻止堆外內存的上漲。其實在這就有點無頭蒼蠅亂撞了。

確實,只有兩種情況會導致netty相關的堆外內存上漲。

1、要么是netty有bug 。

2、要么是使用方法不對。

netty有bug,這個可能性就算了吧。使用的版本也不是最新的,也沒有直接引用netty包,都是通過例如http-client或者rpc框架引入的netty。

使用方法不對?在http client或者rpc服務的部分代碼排查了一遍,基本上都是比較簡單的用法,并沒有直接設置很怪的參數,或者很非常規的操作。

在這就確實在堆里面找不到有用的線索了。

找到原因

貌似確實沒轍了。

隨后我請教了我司的超級大佬:森哥。森哥給我要了相關權限后,上去機器一頓操作。推測可能是C2 Compiler或者什么即時編譯導致的問題,因為堆外都是jvm級別的數據,常規的排查確實比較難找到線索。

聽完后聯想到堆外不就是方法區嗎,我用的java8 hotspot虛擬機,也就是元空間了。

代碼里面會有什么導致元空間上漲呢?

元空間是存儲jvm級別的數據,是否有很多類加載?

帶著這個猜想,找到相應的參數 -verbose:class,這個會將類加載全部打印出來。

如下圖:

圖片圖片

發現有非常多的ASMAccessorImpl_,而且是不會停止,一直在加載。

厚禮蟹,這就查到了原因。

那ASM是什么,如果研究過spring,就知道在aop擴展動態生成字節碼,最底層其實就是ASM生成的,其實是一個字節碼編輯框架。官網:https://asm.ow2.io/。

也就是說,我的代碼有一個地方一直在動態生成類字節碼,加載到方法區。從而導致堆外內存一直在上漲,從而導致full gc。

代碼修改

那怎么定位到是哪段代碼?

這個簡單,打開idea,double shift,調search everywhere。

圖片

排查到是mvel這個依賴框架生成的。

關于mvel,其實是spel差不多,表達式解析引擎。在項目中,mvel的使用我們只用了兩行代碼。

MVEL.executeExpression()
MVEL.compileExpression()

然后我們也有把編譯完的進行緩存,按道理說不會一直生成類的。因為mvel這個框架實在是相關文檔太少,沒人維護的感覺,抱著死馬當活馬醫的態度,去github上提一個issue,然后自己同時接著排查。

圖片

幸運的是這個框架還沒死絕,還有人回復。

大概意思是說,我問為什么使用你們的mvel會導致我jvm出現oom錯誤(頻繁的full gc),另外如果說每次編譯相同的內容的話,為什么沒有框架層面緩存起來。回答說是需要自己緩存的。

也就是我的代碼還是緩存失效了。

找到緩存的那一行,使用的是map,用key去查找的時候,發現用的是contains,而沒有用containsKey。這就導致了永遠查不到,也就導致了永遠會重新編譯。

圖片

經過修改后,問題得以解決。

圖片

一條平平的線,并且沒有full gc,皆大歡喜

圖片

總結

堆外內存有點難搞,難以和代碼聯系起來。提供一個思路:可通過-verbose:class查看類加載的情況,然后具體分析。

責任編輯:姜華 來源: 凱哥的Java技術活
相關推薦

2020-08-27 21:36:50

JVM內存泄漏

2017-01-11 14:02:32

JVM源碼內存

2022-06-15 16:04:13

Java編程語言

2025-06-16 07:40:00

2019-12-17 10:01:40

開發技能代碼

2020-05-09 13:49:00

內存空間垃圾

2014-02-27 13:30:26

CacheLinux系統內存不足

2021-06-01 09:29:43

ArthasJVM內存

2018-11-06 12:12:00

MySQL內存排查

2021-06-28 08:00:00

Python開發編程語言

2019-02-26 14:33:22

JVM內存虛擬機

2022-04-29 08:05:06

內存堆外GC

2024-10-10 15:32:51

2019-02-14 13:30:54

內存泄露運維

2022-11-09 17:10:47

JVM內存區域

2010-09-27 13:41:22

JVM內存回收

2015-07-20 10:23:24

NET內存問題排查

2024-08-19 00:10:00

C++內存

2022-04-15 07:51:12

off-heap堆外內存JVM

2022-09-21 08:39:52

堆外內存泄露內存分布
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品一二三区视频 | 午夜婷婷激情 | 久久精品亚洲精品国产欧美 | 日韩中文一区二区三区 | 国产黑丝av | 国产一区二区欧美 | 国产一区二区三区四区三区四 | 黄色片在线 | 91看片网站| 国产精品日日做人人爱 | 99re视频精品 | 欧美一区二区三区的 | 亚洲精品乱码久久久久v最新版 | 天天操网| 精品国产一区二区三区日日嗨 | 在线成人www免费观看视频 | 国产精品久久久久无码av | av中文字幕网 | 夜夜骑综合 | 欧美理论片在线观看 | 日韩精品在线一区二区 | 欧美一区二区三区在线观看 | 男人天堂网址 | 久久亚洲国产 | 欧美精品久久久久久久久久 | 亚洲精品视频导航 | 久久欧美高清二区三区 | 免费一级欧美在线观看视频 | 欧美jizzhd精品欧美巨大免费 | 7777精品伊人久久精品影视 | 国产91丝袜 | 羞羞视频免费观 | 热99视频| 五月天婷婷狠狠 | 亚洲一区二区三区视频 | 97国产精品 | 日本欧美视频 | 国产美女在线观看 | 99re6在线 | 欧美一区二| 欧美一区永久视频免费观看 |