[英]Virtual Memory Usage from Java under Linux, too much memory used
我有一個在 Linux 下運行的 Java 應用程序的問題。
當我使用默認的最大堆大小 (64 MB) 啟動應用程序時,我看到使用 tops 應用程序為應用程序分配了 240 MB 的虛擬內存。 這會導致計算機上的一些其他軟件出現一些問題,這些軟件相對資源有限。
據我所知,無論如何都不會使用保留的虛擬內存,因為一旦我們達到堆限制,就會拋出OutOfMemoryError
。 我在 windows 下運行了相同的應用程序,我看到虛擬內存大小和堆大小相似。
無論如何,我可以為Linux下的Java進程配置正在使用的虛擬內存嗎?
編輯 1 :問題不在於堆。 問題是,如果我設置一個 128 MB 的堆,例如,Linux 仍然分配 210 MB 的虛擬內存,這是不需要的,永遠。**
編輯 2 :使用ulimit -v
允許限制虛擬內存量。 如果設置的大小低於 204 MB,那么即使不需要 204 MB,只需要 64 MB,應用程序也不會運行。 所以我想了解為什么Java需要這么多的虛擬內存。 這可以改變嗎?
編輯 3 :系統中還有其他幾個應用程序在運行,這些應用程序是嵌入式的。 並且系統確實有虛擬內存限制(來自評論,重要細節)。
這是 Java 長期以來的抱怨,但它在很大程度上毫無意義,而且通常基於查看錯誤的信息。 通常的措辭是“Java 上的 Hello World 需要 10 兆字節!為什么需要那個?” 好吧,這里有一種方法可以讓 64 位 JVM 上的 Hello World 占用超過 4 GB 的空間……至少通過一種測量形式。
java -Xms1024m -Xmx4096m com.example.Hello
在 Linux 上, top命令為您提供了幾個不同的內存數字。 以下是關於 Hello World 示例的說明:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2120 kgregory 20 0 4373m 15m 7152 S 0 0.2 0:00.10 java
Windows 任務管理器的情況稍微復雜一些。 在 Windows XP 下,有“Memory Usage”和“Virtual Memory Size”列,但官方文檔沒有說明它們的含義。 Windows Vista 和 Windows 7 添加了更多的列,並且它們實際上被記錄在案。 其中,“工作集”測量是最有用的; 它大致對應於 Linux 上的 RES 和 SHR 的總和。
進程消耗的虛擬內存是進程內存映射中所有內容的總和。 這包括數據(例如,Java 堆),但也包括程序使用的所有共享庫和內存映射文件。 在 Linux 上,您可以使用pmap命令查看映射到進程空間中的所有內容(從這里開始,我將只參考 Linux,因為它是我使用的;我確定有等效的工具可用於視窗)。 這是“Hello World”程序內存映射的摘錄; 整個內存映射長達100多行,千行列表也不少見。
0000000040000000 36K r-x-- /usr/local/java/jdk-1.6-x64/bin/java 0000000040108000 8K rwx-- /usr/local/java/jdk-1.6-x64/bin/java 0000000040eba000 676K rwx-- [ anon ] 00000006fae00000 21248K rwx-- [ anon ] 00000006fc2c0000 62720K rwx-- [ anon ] 0000000700000000 699072K rwx-- [ anon ] 000000072aab0000 2097152K rwx-- [ anon ] 00000007aaab0000 349504K rwx-- [ anon ] 00000007c0000000 1048576K rwx-- [ anon ] ... 00007fa1ed00d000 1652K r-xs- /usr/local/java/jdk-1.6-x64/jre/lib/rt.jar ... 00007fa1ed1d3000 1024K rwx-- [ anon ] 00007fa1ed2d3000 4K ----- [ anon ] 00007fa1ed2d4000 1024K rwx-- [ anon ] 00007fa1ed3d4000 4K ----- [ anon ] ... 00007fa1f20d3000 164K r-x-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so 00007fa1f20fc000 1020K ----- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so 00007fa1f21fb000 28K rwx-- /usr/local/java/jdk-1.6-x64/jre/lib/amd64/libjava.so ... 00007fa1f34aa000 1576K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so 00007fa1f3634000 2044K ----- /lib/x86_64-linux-gnu/libc-2.13.so 00007fa1f3833000 16K r-x-- /lib/x86_64-linux-gnu/libc-2.13.so 00007fa1f3837000 4K rwx-- /lib/x86_64-linux-gnu/libc-2.13.so ...
格式的快速解釋:每一行都以段的虛擬內存地址開始。 接下來是段大小、權限和段的來源。 最后一項是文件或“匿名”,表示通過mmap分配的內存塊。
從頂部開始,我們有
java
時運行的程序)。 這是非常小的; 它所做的只是加載到存儲真實 JVM 代碼的共享庫中。-Xmx
值分配虛擬內存空間; 這允許它有一個連續的堆。 -Xms
值在內部用於說明程序啟動時“正在使用”的堆有多少,並在接近該限制時觸發垃圾收集。StackOverFlowError
。 對於真正的應用程序,您會看到數十個甚至數百個這些條目在內存映射中重復。 共享庫特別有趣:每個共享庫至少有兩個段:一個包含庫代碼的只讀段,一個包含庫的全局每進程數據的讀寫段(我不知道是什么)沒有權限的段是;我只在 x64 Linux 上看到過)。 庫的只讀部分可以在使用庫的所有進程之間共享; 例如, libc
有 1.5M 的虛擬內存空間可以共享。
虛擬內存映射包含很多東西。 有些是只讀的,有些是共享的,有些是分配的但從未接觸過(例如,在這個例子中幾乎所有的 4Gb 堆)。 但是操作系統足夠聰明,只加載它需要的東西,所以虛擬內存大小在很大程度上無關緊要。
如果您在 32 位操作系統上運行,虛擬內存大小很重要,您只能分配 2Gb(或在某些情況下,3Gb)的進程地址空間。 在這種情況下,您正在處理稀缺資源,並且可能必須進行權衡,例如減少堆大小以對大文件進行內存映射或創建大量線程。
但是,鑒於 64 位機器無處不在,我認為不久之后虛擬內存大小將成為一個完全無關的統計數據。
駐留集大小是實際在 RAM 中的那部分虛擬內存空間。 如果您的 RSS 增長到您的總物理內存的很大一部分,則可能是時候開始擔心了。 如果您的 RSS 增長到占用您所有的物理內存,並且您的系統開始交換,那么該擔心的時間已經過去了。
但是 RSS 也具有誤導性,尤其是在負載較輕的機器上。 操作系統不會花費大量精力來回收進程使用的頁面。 這樣做幾乎沒有什么好處,而且如果進程在將來接觸到頁面,可能會發生代價高昂的頁面錯誤。 因此,RSS 統計信息可能包含許多未處於活動狀態的頁面。
除非您正在交換,否則不要過分擔心各種內存統計信息告訴您什么。 需要注意的是,不斷增長的 RSS 可能表明某種內存泄漏。
對於 Java 程序,關注堆中發生的事情要重要得多。 消耗的空間總量很重要,您可以采取一些步驟來減少它。 更重要的是你花在垃圾收集上的時間,以及堆的哪些部分被收集。
訪問磁盤(即數據庫)很昂貴,而內存很便宜。 如果你可以用一個換另一個,那就這樣做吧。
Java 和 glibc >= 2.10(包括 Ubuntu >= 10.04,RHEL >= 6)存在一個已知問題。
解決方法是設置這個環境。 多變的:
export MALLOC_ARENA_MAX=4
如果您正在運行 Tomcat,您可以將其添加到TOMCAT_HOME/bin/setenv.sh
文件中。
對於 Docker,將此添加到 Dockerfile
ENV MALLOC_ARENA_MAX=4
有一篇關於設置 MALLOC_ARENA_MAX 的 IBM 文章https://www.ibm.com/developerworks/community/blogs/kevgrig/entry/linux_glibc_2_10_rhel_6_malloc_may_show_excessive_virtual_memory_usage?lang=en
眾所周知,常駐內存會以類似於內存泄漏或內存碎片的方式蔓延。
還有一個開放的 JDK 錯誤JDK-8193521“glibc 使用默認配置浪費內存”
在 Google 或 SO 上搜索 MALLOC_ARENA_MAX 以獲取更多參考。
您可能還想調整其他 malloc 選項以優化已分配內存的低碎片化:
# tune glibc memory allocation, optimize for low fragmentation
# limit the number of arenas
export MALLOC_ARENA_MAX=2
# disable dynamic mmap threshold, see M_MMAP_THRESHOLD in "man mallopt"
export MALLOC_MMAP_THRESHOLD_=131072
export MALLOC_TRIM_THRESHOLD_=131072
export MALLOC_TOP_PAD_=131072
export MALLOC_MMAP_MAX_=65536
為 Java 進程分配的內存量與我預期的差不多。 我在嵌入式/內存有限的系統上運行 Java 時遇到過類似的問題。 在具有任意 VM 限制或在沒有足夠交換量的系統上運行任何應用程序往往會中斷。 這似乎是許多現代應用程序的本質,它們不是為在資源有限的系統上使用而設計的。
您還有更多選項可以嘗試限制 JVM 的內存占用。 這可能會減少虛擬內存占用:
-XX:ReservedCodeCacheSize=32m 保留代碼緩存大小(以字節為單位)- 最大代碼緩存大小。 [Solaris 64 位、amd64 和 -server x86:48m; 在 1.5.0_06 及更早版本中,Solaris 64 位和 and64:1024m。]
-XX:MaxPermSize=64m 永久代的大小。 [5.0 及更新版本:64 位虛擬機擴展了 30%; 1.4 amd64:96m; 1.3.1 - 客戶端:32m。]
此外,您還應該將 -Xmx(最大堆大小)設置為盡可能接近應用程序實際峰值內存使用量的值。 我相信 JVM 的默認行為仍然是每次將堆大小擴展到最大值時將堆大小加倍。 如果您從 32M 堆開始並且您的應用程序達到 65M,那么堆最終會增長 32M -> 64M -> 128M。
您也可以嘗試使用此方法來降低 VM 在堆增長方面的積極性:
-XX:MinHeapFreeRatio=40 GC 后堆空閑的最小百分比以避免擴展。
此外,從我幾年前的試驗中回憶起,加載的本機庫的數量對最小占用空間產生了巨大影響。 如果我沒記錯的話,加載 java.net.Socket 增加了超過 15M(我可能沒有)。
Sun JVM 需要大量內存用於 HotSpot,它映射到共享內存中的運行時庫。
如果內存是一個問題,請考慮使用另一個適合嵌入的 JVM。 IBM 有 j9,還有使用 GNU 類路徑庫的開源“jamvm”。 此外,Sun 在 SunSPOTS 上運行了 Squeak JVM,因此還有其他選擇。
減少資源有限的系統的堆空間的一種方法可能是使用 -XX:MaxHeapFreeRatio 變量。 這通常設置為 70,是 GC 收縮前空閑堆的最大百分比。 將其設置為較低的值,您將在例如 jvisualvm 分析器中看到較小的堆通常用於您的程序。
編輯:要為 -XX:MaxHeapFreeRatio 設置較小的值,您還必須設置 -XX:MinHeapFreeRatio 例如
java -XX:MinHeapFreeRatio=10 -XX:MaxHeapFreeRatio=25 HelloWorld
EDIT2:為啟動並執行相同任務的真實應用程序添加了一個示例,一個使用默認參數,一個使用 10 和 25 作為參數。 我沒有注意到任何真正的速度差異,盡管理論上 java 應該使用更多時間來增加后一個例子中的堆。
最后,最大堆為 905,使用的堆為 378
最后,最大堆為 722,使用的堆為 378
這實際上有一些影響,因為我們的應用程序運行在遠程桌面服務器上,許多用戶可能會同時運行它。
只是一個想法,但您可以檢查ulimit -v
選項的影響。
這不是一個實際的解決方案,因為它會限制所有進程可用的地址空間,但這將允許您使用有限的虛擬內存檢查應用程序的行為。
Sun 的 java 1.4 有以下參數來控制內存大小:
-Xmsn 指定內存分配池的初始大小(以字節為單位)。 該值必須是大於 1MB 的 1024 的倍數。 附加字母 k 或 K 以指示千字節,或附加字母 m 或 M 以指示兆字節。 默認值為 2MB。 例子:
-Xms6291456 -Xms6144k -Xms6m
-Xmxn 指定內存分配池的最大大小(以字節為單位)。 該值必須是大於 2MB 的 1024 的倍數。 附加字母 k 或 K 以指示千字節,或附加字母 m 或 M 以指示兆字節。 默認值為 64MB。 例子:
-Xmx83886080 -Xmx81920k -Xmx80m
http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/java.html
Java 5 和 6 還有一些。 請參閱http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp
不,您無法配置 VM 所需的內存量。 但是,請注意,這是虛擬內存,而不是常駐內存,因此如果不實際使用,它只會留在那里而不會造成傷害。
或者,您可以嘗試一些其他的 JVM,然后是 Sun 一個,內存占用更小,但我不能在這里建議。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.