自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Java內(nèi)存故障?只是因?yàn)槟悴粔驇洠?/h1>

開發(fā) 后端
反觀Java,三天兩頭出問題,找人解決還找不到人,給錢都不一定能解決問題。能比么?盤點(diǎn)來盤點(diǎn)去,最后只能靠自己。

從小我就對(duì)Java有著深厚的感情,算下來有幾十年的Java經(jīng)驗(yàn)了。當(dāng)年的Java還是Sun公司的,我有著多年的Servlet經(jīng)驗(yàn),CURD經(jīng)驗(yàn),在現(xiàn)在已經(jīng)被自我革新,轉(zhuǎn)而研究人生的哲學(xué)。罷了,不吹了。本文是關(guān)于Java故障排查的,屬上篇。

為了保證文章的流暢性,我決定一口氣把它寫完。因?yàn)橄嚓P(guān)方面的培訓(xùn)做的多了,就不需要在寫的時(shí)候參考資料、翻源代碼。掐指一算,本文一個(gè)小時(shí)沒花掉,但篇幅已經(jīng)較長(zhǎng)了。

長(zhǎng)了,那就割斷。本篇就定為內(nèi)存排查的上篇,主要講一些原理。為什么要講原理?開車還需要了解汽車結(jié)構(gòu)么?

這還真不能相比。

汽車很少壞,出了問題你會(huì)花錢給拖車公司、4S店。你還會(huì)每年給它買上保險(xiǎn)。

反觀Java,三天兩頭出問題,找人解決還找不到人,給錢都不一定能解決問題。能比么?盤點(diǎn)來盤點(diǎn)去,最后只能靠自己。

  • 內(nèi)存里都有啥
  • 操作系統(tǒng)內(nèi)存
  • JVM內(nèi)存劃分
  • 一圖解千愁,jvm內(nèi)存從來沒有這么簡(jiǎn)單過!
  • 為什么會(huì)有內(nèi)存問題
  • 垃圾回收器
  • 重要概念GC Roots
  • 對(duì)象的提升

1. 內(nèi)存里都有啥

要想排查內(nèi)存問題,我們就需要看一下內(nèi)存里都有啥。我們先來看一下操作系統(tǒng)內(nèi)存的劃分,然后再來看一下JVM內(nèi)存的劃分。由于JVM本身是作為一個(gè)正常的應(yīng)用運(yùn)行在操作系統(tǒng)上的,所以它的行為同時(shí)會(huì)受到操作系統(tǒng)的限制。

2. 操作系統(tǒng)內(nèi)存

我們首先從操作系統(tǒng)的實(shí)現(xiàn)來說起。通常情況下,我們寫了一個(gè)C語言程序,編譯后,會(huì)發(fā)現(xiàn)里面的內(nèi)存地址是固定的。其實(shí)我們的應(yīng)用程序在編譯之后,這些地址都是虛擬地址。他需要經(jīng)過一層翻譯之后,才能映射到真正的物理內(nèi)存,MMU就是負(fù)責(zé)地址轉(zhuǎn)換的硬件。

那我們操作系統(tǒng)的可用內(nèi)存到底是多少呢?它其實(shí)是分為兩部分的。一部分是物理內(nèi)存,指的是我們插的那根內(nèi)存條;另一部分就是使用磁盤模擬的虛擬內(nèi)存,在Linux通常稱做swap分區(qū)。所以,可用內(nèi)存 = 物理內(nèi)存 + 虛擬內(nèi)存。如果你的系統(tǒng)開了swap,可用內(nèi)存就比物理內(nèi)存大。

通過top命令和free命令都可以看到內(nèi)存的使用情況。

top命令可以看到每一個(gè)進(jìn)程的內(nèi)存使用情況,我們平常關(guān)注的是RES這一列,它代表的是進(jìn)程實(shí)際的內(nèi)存占用,我們平常在搭建監(jiān)控系統(tǒng)的時(shí)候,監(jiān)控的也是這個(gè)數(shù)值。

我們?cè)賮砜匆幌耭ree命令的展示。它的展示其實(shí)是有一些混亂的,具體的關(guān)系可以看上面的圖。通常情況下,free顯示的數(shù)值都是比較小的,但這并不等于系統(tǒng)的可用內(nèi)存就那么一點(diǎn)點(diǎn)。Linux操作系統(tǒng)啟動(dòng)后,隨著機(jī)器的運(yùn)行,剩余內(nèi)存會(huì)迅速被buffer和cache這些緩沖區(qū)和緩存迅速占滿,而這些內(nèi)存再應(yīng)用的內(nèi)存空間不足時(shí),是可以釋放的。可用內(nèi)存 = free + buffers + cached。

具體每一個(gè)區(qū)域的內(nèi)存使用情況,可以通過/proc/meminfo進(jìn)行查看的。

  1. # cat /proc/meminfo 
  2. MemTotal:        3881692 kB 
  3. MemFree:          249248 kB 
  4. MemAvailable:    1510048 kB 
  5. Buffers:           92384 kB 
  6. Cached:          1340716 kB 
  7. 40+ more ... 

3. JVM內(nèi)存劃分

接下來,我們才來看一下JVM的內(nèi)存區(qū)域劃分。

在JVM中,最大的內(nèi)存區(qū)域就是堆,我們平常創(chuàng)建的大部分對(duì)象,都會(huì)存放在這里。所謂的垃圾回收,也主要針對(duì)的是這一部分。

多本JVM書籍描述:JVM中,除了程序計(jì)數(shù)器,其他區(qū)域都是可能溢出的。我們這里依然同意這個(gè)結(jié)論。下面僅對(duì)這些內(nèi)存區(qū)域做簡(jiǎn)要的介紹,因?yàn)橛行┲R(shí)對(duì)我們的內(nèi)存排查無益。

  • 堆:JVM堆中的數(shù)據(jù),是共享的,是占用內(nèi)存最大的一塊區(qū)域
  • 虛擬機(jī)棧:Java虛擬機(jī)棧,是基于線程的,用來服務(wù)字節(jié)碼指令的運(yùn)行
  • 程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器
  • 元空間:方法區(qū)就在這里,非堆 本地內(nèi)存:其他的內(nèi)存占用空間

類比上面這張圖,我們可以歸位一些常用對(duì)象的分配位置。不要糾結(jié)什么棧上分配逃逸分析,也不用關(guān)注棧幀和操作數(shù)棧這種雙層的結(jié)構(gòu),這些小細(xì)節(jié)對(duì)于對(duì)象的汪洋大海來說,影響實(shí)在是太小。我們關(guān)注的內(nèi)存區(qū)域,其實(shí)就只有堆內(nèi)內(nèi)存和堆外內(nèi)存兩個(gè)概念。

4. 一圖解千愁,jvm內(nèi)存從來沒有這么簡(jiǎn)單過!

5. 為什么會(huì)有內(nèi)存問題

統(tǒng)計(jì)顯示,我們平常的工作中,OOM/ML問題占比5%左右,平均處理時(shí)間卻達(dá)到40天左右。這就可以看出這種問題的排查,是非常的困難的。

但讓人無語的是,遇到內(nèi)存問題,工程師們的現(xiàn)場(chǎng)保護(hù)意識(shí)往往不足,特別的不足。只知道一個(gè)內(nèi)存溢出的結(jié)果,但什么都沒留下。監(jiān)控沒有,日志沒有,甚至連發(fā)生的時(shí)間點(diǎn)都不清楚。這樣的問題,鬼才知道原因。

6. 垃圾回收器

內(nèi)存問題有兩種模式,一種是內(nèi)存溢出,一種是內(nèi)存泄漏。

  • 內(nèi)存溢出 OutOfMemoryError,簡(jiǎn)稱OOM,堆是最常見的情況,堆外內(nèi)存排查困難。
  • 內(nèi)存泄漏 Memory Leak,簡(jiǎn)稱ML,主要指的是分配的內(nèi)存沒有得到釋放。內(nèi)存一直在增長(zhǎng),有OOM風(fēng)險(xiǎn);GC時(shí)該回收的回收不掉;或者能夠回收掉但很快又占滿,產(chǎn)生壓力。

內(nèi)存問題影響也是非常大的,比如下面這三種場(chǎng)景。

  • 發(fā)生OOM Error,應(yīng)用停止(最嚴(yán)重)
  • 頻繁GC,GC時(shí)間長(zhǎng),GC線程時(shí)間片占用高
  • 服務(wù)卡頓,請(qǐng)求響應(yīng)時(shí)間變長(zhǎng)

說到這卡頓問題,就不得不提一嘴垃圾回收器。

很多同學(xué)一看上面的圖,就知道我們要說G1垃圾回收器了,這也是我的推薦。CMS等垃圾回收器,回收時(shí)間不可控,如果你有條件,當(dāng)然要避免使用,CMS也將要在Java14中被移除,我也真心不希望你掌握一些即將過時(shí)的經(jīng)驗(yàn)。ZGC雖然厲害,但還太新,幾乎沒有人敢吃螃蟹,那剩下的就是G1了。

G1通過三個(gè)簡(jiǎn)單的配置參數(shù),大部分情況下即可獲取優(yōu)異的性能,工程師幸福了很多。三個(gè)參數(shù)如下:

  • MaxGCPauseMillis 預(yù)定目標(biāo),自動(dòng)調(diào)整。
  • G1HeapRegionSize 小堆區(qū)大小。
  • InitiatingHeapOccupancyPercent 堆內(nèi)存比例閾值,啟動(dòng)并發(fā)標(biāo)記。

如果你還是不放心,想要了解一下G1的原理,那我們也可以捎帶提上兩嘴。G1其實(shí)還是有年輕代老年代的概念的,只不過它的內(nèi)存是不連續(xù)的。

如圖所示,G1將內(nèi)存切分成大小相等的區(qū)域,這些區(qū)域叫做小堆區(qū),是垃圾回收的最小單位。以前的垃圾回收器都是整代回收,而G1是部分回收,那就可以根據(jù)配置的最小延遲時(shí)間合理的選取小堆區(qū)的數(shù)量,回收過程就顯得智能了很多。

7. 重要概念GC Roots

如圖所示,要確定哪些是垃圾,就需要有一種找到垃圾的方法。其實(shí),我們上一句的表述是不正確的。在JVM中,找垃圾的方法和我們理解的正好相反:它是首先找到存活的對(duì)象,對(duì)存活的對(duì)象做標(biāo)記,然后把其他對(duì)象一股腦的回收掉。

JVM在垃圾回收時(shí),關(guān)心的是不要把不是垃圾的對(duì)象給回收了,而不是把垃圾對(duì)象給清理的干干凈凈。

要找到哪些是存活對(duì)象,就需要從源頭上追溯。在JVM中,常見的GC Roots就有靜態(tài)的成員變量等,比如一個(gè)靜態(tài)的HashMap。

另外一部分,就是線程所關(guān)聯(lián)的虛擬機(jī)棧和本地方法棧里面的內(nèi)容。

我們說了這老半天,其實(shí)這種追溯方式有一個(gè)專有的名詞:可達(dá)性分析法。與之類似的還有引用計(jì)數(shù)法,但由于有環(huán)形依賴的問題,所以幾乎沒有回收器使用這種形式。

并不是說只要是和GC Roots有一條聯(lián)系(Reference Chain),對(duì)象就是存活的,它還與對(duì)象的引用級(jí)別有關(guān)。

  • 強(qiáng)引用:屬于最普通最強(qiáng)硬的一種存在,只有在和GC Roots斷絕關(guān)系時(shí),才會(huì)被消滅掉
  • 軟引用:只有在內(nèi)存不足時(shí),系統(tǒng)則會(huì)回收軟引用對(duì)象
  • 弱引用:當(dāng)JVM進(jìn)行垃圾回收時(shí),無論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象
  • 虛引用:虛引用主要用來跟蹤對(duì)象被垃圾回收的活動(dòng)

平常情況下,我們使用的對(duì)象就是強(qiáng)引用。軟引用和弱引用在一些緩存框架中用的比較廣泛,對(duì)象的重要程度也比較弱。

8. 對(duì)象的提升

大多數(shù)垃圾回收器都是分代垃圾回收,我們從上面對(duì)G1的描述就能夠看出來。

如圖所示,是典型的分代回收內(nèi)存模型。對(duì)象從年輕代提升到老年代,有四種方式。

  • 常規(guī)提升,對(duì)象夠老。比如從from到to轉(zhuǎn)了15圈還沒有被回收掉??刂茀?shù)就是-XX:MaxTenuringThreshold。這個(gè)值在CMS下默認(rèn)為6,G1下默認(rèn)為15
  • 分配擔(dān)保 Survivor 空間不夠,老年代擔(dān)保。
  • 大對(duì)象直接在老年代分配
  • 動(dòng)態(tài)對(duì)象年齡判定。比如在G1里的TenuringThreshold會(huì)隨著堆內(nèi)對(duì)象的分布而變化

對(duì)于垃圾回收器的優(yōu)化,就是要確保盡量多的對(duì)象在年輕代里分配,減少對(duì)象提升到老年代的可能。雖然這種思想在G1里弱化了許多。

End了解了操作系統(tǒng)的內(nèi)存里都有啥,又了解了JVM的內(nèi)存里都有啥,我們就可以淡定縱容的針對(duì)于每一種出現(xiàn)問題的情況,進(jìn)行針對(duì)性排查和優(yōu)化。

文章到這里嘎然而止。下一篇,我們以幾個(gè)實(shí)際的案例,來看一下Java的內(nèi)存問題排查的具體過程。

 

責(zé)任編輯:趙寧寧 來源: 小姐姐味道
相關(guān)推薦

2020-07-27 08:08:47

Java內(nèi)存JVM

2009-12-28 09:33:29

ChromeGoogle首頁(yè)

2015-04-14 10:39:09

iWatch蘋果

2010-10-26 10:37:31

Java之父蘋果

2018-01-18 15:15:49

程序員辭職委屈

2022-07-29 08:40:20

設(shè)計(jì)模式責(zé)任鏈場(chǎng)景

2018-07-31 14:03:09

JVM內(nèi)存數(shù)據(jù)

2014-11-04 10:15:28

Android

2021-04-27 22:38:41

代碼開發(fā)前端

2017-10-31 09:59:15

互聯(lián)網(wǎng)商業(yè)數(shù)據(jù)

2022-12-12 09:46:49

Kubernetes容器

2021-04-07 17:06:55

String Final存儲(chǔ)

2013-06-13 08:58:02

iOS7WWDCDesign By C

2018-03-07 18:14:07

物聯(lián)網(wǎng)信息網(wǎng)絡(luò)

2015-08-05 14:33:01

APP用戶原因

2021-06-09 10:59:13

數(shù)字化轉(zhuǎn)型CIO數(shù)字化

2020-05-26 16:56:06

人工智能

2012-05-28 10:47:33

跳槽程序員

2012-02-09 09:04:08

數(shù)據(jù)中心外包云計(jì)算

2012-02-07 13:28:55

云計(jì)算數(shù)據(jù)中心
點(diǎn)贊
收藏

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