使用未初始化的内存是什么意思_他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢...
這確實(shí)是個(gè)挺奇怪的問題,特別是當(dāng)最常出現(xiàn)的幾種解釋理由都被排除后,看來JVM并沒有耍一些明顯的小花招:
- -Xmx和-Xms是相等的,因此檢測結(jié)果并不會(huì)因?yàn)槎褍?nèi)存增加而在運(yùn)行時(shí)有所變化。
- 通過關(guān)閉自適應(yīng)調(diào)整策略(-XX:-UseAdaptiveSizePolicy),JVM已經(jīng)事先被禁止動(dòng)態(tài)調(diào)整內(nèi)存池的大小。
重現(xiàn)差異檢測結(jié)果
要弄清楚這個(gè)問題的第一步就是要明白這些工具的實(shí)現(xiàn)原理。通過標(biāo)準(zhǔn)APIs,我們可以用以下簡單語句得到可使用的內(nèi)存信息。
System.out.println("Runtime.getRuntime().maxMemory()="+Runtime.getRuntime().maxMemory());而且確實(shí),現(xiàn)有檢測工具底層也是用這個(gè)語句來進(jìn)行檢測。要解決這個(gè)問題,首先我們需要一個(gè)可重復(fù)使用的測試用例。因此,我寫了下面這段代碼:
package?eu.plumbr.test;//imports?skipped?for?brevitypublic?class?HeapSizeDifferences?{??static?Collection?objects?=?new?ArrayList();??static?long?lastMaxMemory?=?0;??public?static?void?main(String[]?args)?{????try?{??????List?inputArguments?=?ManagementFactory.getRuntimeMXBean().getInputArguments();??????System.out.println("Running?with:?"?+?inputArguments);??????while?(true)?{????????printMaxMemory();????????consumeSpace();??????}????}?catch?(OutOfMemoryError?e)?{??????freeSpace();??????printMaxMemory();????}??}??static?void?printMaxMemory()?{????long?currentMaxMemory?=?Runtime.getRuntime().maxMemory();????if?(currentMaxMemory?!=?lastMaxMemory)?{??????lastMaxMemory?=?currentMaxMemory;??????System.out.format("Runtime.getRuntime().maxMemory():?%,dK.%n",?currentMaxMemory?/?1024);????}??}??static?void?consumeSpace()?{????objects.add(new?int[1_000_000]);??}??static?void?freeSpace()?{????objects.clear();??}}這段代碼通過將new int[1_000_000]置于一個(gè)循環(huán)中來不斷分配內(nèi)存給程序,然后監(jiān)測JVM運(yùn)行期的當(dāng)前可用內(nèi)存。當(dāng)程序監(jiān)測到可用內(nèi)存大小發(fā)生變化時(shí),通過打印出Runtime.getRuntime().maxMemory()返回值來得到當(dāng)前可用內(nèi)存尺寸,輸出類似下面語句:
Running?with:?[-Xms2048M,?-Xmx2048M]Runtime.getRuntime().maxMemory():?2,010,112K.實(shí)際情況也確實(shí)如預(yù)估的那樣,盡管我已經(jīng)給JVM預(yù)先指定分配了2G對內(nèi)存,在不知道為什么在運(yùn)行期有85M內(nèi)存不見了。你大可以把 Runtime.getRuntime().maxMemory()的返回值2,010,112K 除以1024來轉(zhuǎn)換成MB,那樣你將得到1,963M,正好和2048M差85M。
找到根本原因
在成功重現(xiàn)了這個(gè)問題之后,我嘗試用使用不同的GC算法,果然檢測結(jié)果也不盡相同。
除了G1算法剛好完整使用了我預(yù)指定分配的2G之外,其余每種GC算法似乎都不同程度地丟失了一些內(nèi)存。
現(xiàn)在我們就該看看在JVM的源代碼中有沒有關(guān)于這個(gè)問題的解釋了。我在CollectedHeap這個(gè)類的源代碼中找到了如下的解釋:
??Running?with:?[-Xms2048M,?-Xmx2048M]??//?Support?for?java.lang.Runtime.maxMemory():??return?the?maximum?amount?of??//?memory?that?the?vm?could?make?available?for?storing?'normal'?java?objects.??//?This?is?based?on?the?reserved?address?space,?but?should?not?include?space??//?that?the?vm?uses?internally?for?bookkeeping?or?temporary?storage??//?(e.g.,?in?the?case?of?the?young?gen,?one?of?the?survivor??//?spaces).??virtual?size_t?max_capacity()?const?=?0;我不得不說這個(gè)答案藏得有點(diǎn)深,但是只要你有足夠的好奇心,還是不難發(fā)現(xiàn)的:有時(shí)候,有一塊Survivor區(qū)是不被計(jì)算到可用內(nèi)存中的。
明白這一點(diǎn)之后問題就好解決了。打開并查看GC logging 信息之后我們發(fā)現(xiàn),在Serial,Parallel以及CMS算法回收過程中丟失的那些內(nèi)存,尺寸剛好等于JVM從2G堆內(nèi)存中劃分給Survivor區(qū)內(nèi)存的尺寸。例如,在上面的ParallelGC算法運(yùn)行時(shí),GC logging信息如下:
Running?with:?[-Xms2g,?-Xmx2g,?-XX:+UseParallelGC,?-XX:+PrintGCDetails]Runtime.getRuntime().maxMemory():?2,010,112K....?rest?of?the?GC?log?skipped?for?brevity?...?PSYoungGen??????total?611840K,?used?524800K?[0x0000000795580000,?0x00000007c0000000,?0x00000007c0000000)??eden?space?524800K,?100%?used?[0x0000000795580000,0x00000007b5600000,0x00000007b5600000)??from?space?87040K,?0%?used?[0x00000007bab00000,0x00000007bab00000,0x00000007c0000000)??to???space?87040K,?0%?used?[0x00000007b5600000,0x00000007b5600000,0x00000007bab00000)?ParOldGen???????total?1398272K,?used?1394966K?[0x0000000740000000,?0x0000000795580000,?0x0000000795580000)由上面的信息可以看出,Eden區(qū)被分配了524,800K,兩個(gè)Survivor區(qū)都被分配到了87,040K,老年代(Old space)則被分配了1,398,272K。把Eden區(qū)、老年代以及一個(gè)Survivor區(qū)的尺寸求和,剛好等于2,010,112K,說明丟失的那85M(87,040K)確實(shí)就是剩下的那個(gè)Survivor區(qū)。
總結(jié)
讀完這篇帖子的你現(xiàn)在應(yīng)該對如何探索Java API的實(shí)現(xiàn)原理有了一些新的想法。下次當(dāng)你用某個(gè)可視化工具查看可用堆內(nèi)存發(fā)現(xiàn)所得的結(jié)果略少于-Xmx指定分配的大小時(shí),你就知道這兩者之間的差值是一塊Survivor區(qū)的大小。
私信回復(fù) 資料 領(lǐng)取一線大廠Java面試題總結(jié)+阿里巴巴泰山手冊+各知識點(diǎn)學(xué)習(xí)思維導(dǎo)+一份300頁pdf文檔的Java核心知識點(diǎn)總結(jié)!
這些資料的內(nèi)容都是面試時(shí)面試官必問的知識點(diǎn),篇章包括了很多知識點(diǎn),其中包括了有基礎(chǔ)知識、Java集合、JVM、多線程并發(fā)、spring原理、微服務(wù)、Netty 與RPC 、Kafka、日記、設(shè)計(jì)模式、Java算法、數(shù)據(jù)庫、Zookeeper、分布式緩存、數(shù)據(jù)結(jié)構(gòu)等等。
我必須承認(rèn)這個(gè)知識點(diǎn)在日常編程中并不是特別常用,但這并不是這篇帖子的重點(diǎn)。我寫下這篇帖子是為了描述一種特質(zhì),一種我經(jīng)常在優(yōu)秀的程序員身上尋找的特質(zhì)-好奇心。好的程序員們會(huì)經(jīng)常試著去了解一些事物工作的機(jī)理以及原因。有時(shí)問題的答案并不會(huì)那么顯而易見,但是希望你能堅(jiān)持尋找下去,最終在尋找過程中的所累積的知識總會(huì)讓你獲益匪淺。
總結(jié)
以上是生活随笔為你收集整理的使用未初始化的内存是什么意思_他们都说JVM能实际使用的内存比-Xmx指定的少?这是为什么呢...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 企业年报在哪里查
- 下一篇: 什么股东具有优先认股权