为什么我的JVM访问的内存少于通过-Xmx指定的内存?
“嘿,你能來看看奇怪的東西嗎?” 這就是我開始研究一個(gè)支持案例的方式,該案例將我引向了這篇博客文章。 當(dāng)前的特殊問題與不同的工具報(bào)告了有關(guān)可用內(nèi)存的不同數(shù)字有關(guān)。
簡而言之,一位工程師正在研究特定應(yīng)用程序的過多內(nèi)存使用情況,據(jù)他所知,該應(yīng)用程序可以使用2G的堆。 但是無論出于什么原因,JVM工具本身似乎都沒有決定該進(jìn)程真正擁有多少內(nèi)存。 例如, jconsole猜測總可用堆等于1,963M,而jvisualvm聲稱其等于2,048M。 那么哪個(gè)工具是正確的,為什么另一個(gè)卻顯示不同的信息呢?
確實(shí)確實(shí)很奇怪,尤其是看到通常的可疑對象都被淘汰了– JVM并沒有采取任何明顯的技巧,例如:
- -Xmx和-Xms相等,因此在運(yùn)行時(shí)堆增加期間報(bào)告的數(shù)字不會更改
- 通過關(guān)閉自適應(yīng)大小調(diào)整策略( -XX:-UseAdaptiveSizePolicy ),防止JVM動(dòng)態(tài)調(diào)整內(nèi)存池大小。
重現(xiàn)差異
理解問題的第一步是放大工具實(shí)現(xiàn)。 通過標(biāo)準(zhǔn)API訪問可用的內(nèi)存信息非常簡單,如下所示:
System.out.println("Runtime.getRuntime().maxMemory()="+Runtime.getRuntime().maxMemory());確實(shí),這就是手頭工具似乎正在使用的工具。 回答此類問題的第一步是擁有可重現(xiàn)的測試用例。 為此,我編寫了以下代碼段:
package eu.plumbr.test; //imports skipped for brevitypublic class HeapSizeDifferences {static Collection<Object> objects = new ArrayList<Object>();static long lastMaxMemory = 0;public static void main(String[] args) {try {List<String> 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();} }該代碼通過循環(huán)中的新int [1_000_000]分配內(nèi)存塊,并檢查當(dāng)前已知可用于JVM運(yùn)行時(shí)的內(nèi)存。 每當(dāng)發(fā)現(xiàn)最后一個(gè)已知的內(nèi)存大小發(fā)生更改時(shí),它都會通過打印Runtime.getRuntime()。maxMemory()的輸出來報(bào)告該更改,如下所示:
Running with: [-Xms2048M, -Xmx2048M] Runtime.getRuntime().maxMemory(): 2,010,112K.確實(shí)- 即使我已指定JVM使用2G堆,運(yùn)行時(shí)仍無法以某種方式找到其中的85M 。 您可以通過將Runtime.getRuntime()。maxMemory()的輸出除以2,010,112K除以1024來轉(zhuǎn)換為MB,從而仔細(xì)檢查我的數(shù)學(xué)運(yùn)算。結(jié)果將等于1,963M,與2048M的差值為85M。
尋找根本原因
在能夠重現(xiàn)案例之后,我記下了以下筆記–使用不同的GC算法運(yùn)行似乎也會產(chǎn)生不同的結(jié)果:
| -XX:+ UseSerialGC | 2,027,264千 |
| -XX:+ UseParallelGC | 2,010,112千 |
| -XX:+ UseConcMarkSweepGC | 2,063,104千 |
| -XX:+ UseG1GC | 2,097,152千 |
除了G1完全消耗了我給該進(jìn)程分配的2G內(nèi)存外,其他所有GC算法似乎都始終丟失半隨機(jī)的內(nèi)存。
現(xiàn)在是時(shí)候深入研究JVM 的源代碼了,在CollectedHeap的源代碼中,我發(fā)現(xiàn)了以下內(nèi)容:
// 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;我不得不承認(rèn)答案是非常隱蔽的。 但是仍然有一些真正的好奇心可以找到的暗示–指的是在某些情況下,堆大小計(jì)算中可能會排除一個(gè)幸存空間 。
從這里一直是順風(fēng)順?biāo)C打開GC日志記錄發(fā)現(xiàn),實(shí)際上,使用2G堆,串行,并行和CMS算法都將幸存空間的大小設(shè)置為恰好缺少了差異。 例如,在上面的ParallelGC示例中,GC日志記錄演示了以下內(nèi)容:
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空間設(shè)置為524,800K,兩個(gè)幸存者空間(從和到)都設(shè)置為87,040K,舊空間的大小為1,398,272K。 將Eden,Old和一個(gè)幸存者空間加在一起總計(jì)為2,010,112K,這證實(shí)丟失的85M或87,040K確實(shí)是剩余的幸存者空間 。
摘要
閱讀該文章后,您現(xiàn)在對Java API實(shí)施細(xì)節(jié)有了新的了解。 下次某些工具將總可用堆大小可視化為略小于Xmx指定的堆大小時(shí),您會知道差異等于您的Survivor空間之一的大小。
我必須承認(rèn),這一事實(shí)在日常編程活動(dòng)中并不是特別有用,但這不是該帖子的重點(diǎn)。 取而代之的是,我寫了一篇文章,描述了我一直在優(yōu)秀工程師中尋找的一個(gè)特殊特性- 好奇心 。 優(yōu)秀的工程師一直在尋找了解事物如何以及為什么以某種方式起作用的方式。 有時(shí)答案仍然隱藏,但我仍然建議您嘗試尋求答案。 最終,沿途積累的知識將開始帶來紅利。
翻譯自: https://www.javacodegeeks.com/2015/02/jvm-access-less-memory-specified-via-xmx.html
總結(jié)
以上是生活随笔為你收集整理的为什么我的JVM访问的内存少于通过-Xmx指定的内存?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器安装linux系统安装教程(服务器
- 下一篇: 写安卓程序用什么语言(写安卓程序)