簡體   English   中英

Java:為什么使用固定數量的內存? 或它如何管理內存?

[英]Java: why does it uses a fixed amount of memory? or how does it manage the memory?

看來JVM使用了一些固定數量的內存。 至少我經常看到參數-Xmx (用於最大大小)和-Xms (用於初始大小)表明了這一點。

我感到Java應用程序不能很好地處理內存。 我注意到了一些事情:

  • 即使是一些非常小的示例演示應用程序也會加載大量的內存。 也許是由於Java庫已加載。 但是為什么需要為每個Java實例加載庫? (之所以這樣,是因為多個小型應用程序線性地占用了更多的內存。有關我描述此問題的詳細信息,請參見此處 。)或者為什么這樣做呢?

  • 像Eclipse這樣的大型Java應用程序經常會因某些OutOfMemory異常而崩潰。 這總是很奇怪,因為我的系統上仍然有足夠的可用內存。 通常,它們在運行時會消耗越來越多的內存。 我不確定它們是否存在某些內存泄漏,或者是由於內存池中的碎片造成的,我感覺后者就是這種情況。

  • 與類似Qt之類的功能強大的庫相比,Java庫似乎需要更多的內存。 為什么是這樣? (作為比較,啟動一些Qt應用程序,查看它們的內存使用情況,然后啟動一些Java應用程序。)

為什么它不只使用諸如mallocfree類的基礎系統技術? 或者,如果他們不喜歡libc實現,則可以使用jemalloc (例如在FreeBSD和Firefox中 ),這似乎非常不錯。 我非常確定這將比JVM內存池更好。 而且,不僅性能更好,而且需要更少的內存,尤其是。 適用於小型應用。


另外:有人已經嘗試過了嗎? 我會對基於LLVM的Java JIT編譯器非常感興趣,該編譯器僅使用malloc / free進行內存處理。

也許這也因JVM實現而有所不同? 我主要使用Sun JVM。

(還要注意:我在這里不是直接談論GC。GC僅負責計算可以刪除哪些對象並初始化內存釋放,但實際的釋放是一個不同的子系統。Afaik,它是一些自己的內存池實施,而不僅僅是致電free 。)


編輯:一個非常相關的問題: (Sun)JVM為什么對內存使用量有固定的上限? 或換句話說:為什么JVM處理內存分配的方式與本機應用程序不同?

您需要記住,垃圾收集器所做的不僅是收集無法訪問的對象。 它還優化了堆空間,並准確跟蹤了用於創建新對象的內存確切位置

立即知道哪里有可用內存,可以高效地將新對象分配給年輕一代,並避免了往返於底層OS的需求。 Sun的Jon Masamitsu表示,JIT編譯器還在JVM層之外優化了此類分配:

快速路徑分配不會調用JVM來分配對象。 JIT編譯器知道如何從年輕代中進行分配,並且為對象分配內聯生成用於分配的代碼。 解釋器還知道如何在不調用VM的情況下進行分配。

請注意,JVM也竭盡全力嘗試獲取大的連續內存塊,這可能有其自身的性能優勢 (請參閱“丟失緩存的代價”)。 我想象對malloc (或替代方案)的調用在malloc調用之間提供連續內存的可能性很小,但也許我錯過了一些東西。

此外,通過維護內存本身,垃圾收集器可以根據使用情況和訪問模式進行分配優化。 現在,我不知道它在多大程度上做到了這一點,但是鑒於該概念已經獲得了Sun的注冊專利 ,我想他們已經做了一些事情。

保持分配這些內存塊也為Java程序提供了保障。 由於垃圾回收對程序員來說是隱藏的,因此他們無法告訴JVM“不,請保留該內存;我已經完成了這些對象,但是我需要用於新對象的空間。” 通過保留內存,GC不會放棄無法恢復的內存。 自然地,您總是可以通過任何一種方式獲得OutOfMemoryException ,但是似乎在每次處理完一個對象時都不必要地將內存歸還給操作系統是更合理的選擇,因為您已經麻煩自己獲取了。

除了所有這些,我將嘗試直接解決您的一些評論:

通常,它們在運行時會消耗越來越多的內存。

假設這不只是程序正在執行的操作(出於某種原因,也許有泄漏,也許它必須跟蹤越來越多的數據),我想這與空閑哈希空間比率有關(Sun / Oracle)JVM設置的默認值。 -XX:MinHeapFreeRatio的默認值為40%,而-XX:MaxHeapFreeRatio的默認值為70%。 這意味着只要剩余40%的堆空間,堆就會通過從操作系統中聲明更多的內存來重新調整大小(前提是這不會超過-Xmx )。 相反,如果可用空間超過70%,它將僅*將堆內存釋放回操作系統。

考慮一下如果我在Eclipse中運行內存密集型操作會怎樣? 例如,分析。 我的內存消耗將激增,並在整個過程中調整堆大小(可能多次)。 一旦完成,內存需求就會回落,但不會下降到足以使70%的堆可用。 這意味着現在分配了許多未充分利用的空間,JVM不想釋放這些空間。 這是一個主要缺點,但是您可以通過根據情況自定義百分比來解決此問題。 為了更好地了解這一點,您確實應該對應用程序進行概要分析,以便可以查看已利用堆空間與已分配堆空間。 我個人使用YourKit ,但是有很多不錯的選擇。

*我不知道這是否真的是唯一的時間,以及從OS的角度來看如何觀察到,但是文檔說這是“ GC之后避免收縮的最大堆可用百分比”,這似乎暗示了這一點。

即使是一些非常小的示例演示應用程序也會加載大量的內存。

我猜這取決於它們是哪種應用程序。 我覺得Java GUI應用程序運行着大量內存,但是我沒有任何證據可以證明。 您是否有我們可以看的具體示例?

但是為什么需要為每個Java實例加載庫?

好吧,如果不創建新的JVM進程,您將如何處理多個Java應用程序的加載? 隔離進程是一件好事,這意味着可以獨立加載。 不過,我認為對於一般流程而言,這並不罕見。

最后一點,您在另一個問題中詢問的緩慢啟動時間可能來自達到基線應用程序內存需求所必需的幾個初始堆重新分配(由於-Xms-XX:MinHeapFreeRatio ),具體取決於默認值是您的JVM。

Java在虛擬機內部運行,這限制了其行為的許多部分。 注意術語“虛擬機”。 從字面上看,它好像機器是一個單獨的實體一樣在運行,而基礎機器/ OS只是資源。 -Xmx值定義VM將擁有的最大內存量,而-Xms值定義應用程序可用的起始內存。

VM是二進制文件的一部分,是系統不可知的-這是一種用於允許字節碼在任何地方執行的解決方案。 這類似於模擬器-對於舊游戲系統而言。 它模擬了運行游戲的“機器”。

遇到OutOfMemoryException的原因是因為虛擬機已達到-Xmx限制-它實際上已經用完了內存。

就較小的程序而言,它們通常需要更大比例的內存用於VM。 而且,Java具有默認的開始-Xmx和-Xms(我現在不記得它們的含義),它將始終以它開頭。 當您開始構建和運行“實際”應用程序時,VM和庫的開銷變得不那么明顯了。

與QT等有關的內存參數是正確的,但並非全部。 盡管它使用的內存比其中的更多,但這些內存是為特定體系結構編譯的。 自從我使用QT或類似的庫以來已經有一段時間了,但是我記得內存管理不是很健壯,並且內存泄漏在C / C ++程序中仍然很普遍。 垃圾回收的好處在於,它消除了許多導致內存泄漏的常見“陷阱”。 (注意:並非全部。在Java中仍然有可能泄漏內存,只是有點困難)。

希望這有助於消除您可能一直遇到的困惑。

回答部分問題;

Java在啟動時會分配“堆”內存或固定大小的塊(-Xms參數)。 它實際上並沒有立即使用所有這些內存,但是它告訴操作系統“我想要這么多的內存”。 然后,當您創建對象並在Java環境中工作時,它將創建的對象放入此預分配的內存堆中。 如果該內存塊已滿,則它將向操作系統請求更多的內存,直到達到“最大堆大小”(-Xmx參數)為止。

一旦達到最大大小,即使有很多可用空間,Java也將不再從操作系統請求更多RAM。 如果嘗試創建更多對象,則將沒有堆空間,並且將收到OutOfMemory異常。 現在,如果您正在查看Windows Task Manager或類似的東西,您將看到使用X內存的“ java.exe”。 這種排序對應於它為堆請求的內存量,而不是實際使用的堆中的內存量。

換句話說,我可以編寫應用程序:

class myfirstjavaprog
{  
    public static void main(String args[])
    {
       System.out.println("Hello World!");
    }
}

這基本上將占用很少的內存。 但是,如果我使用cmd行運行它:

java.exe myfirstjavaprog -Xms 1024M

然后在啟動時,java會立即向操作系統要求1,024 MB的內存,這就是Windows Task Manager中將顯示的內容。 實際上,不使用該ram,但是java保留了該ram供以后使用。

相反,如果我有一個嘗試創建10,000字節大數組的應用程序:

class myfirstjavaprog
{  
    public static void main(String args[])
    {
       byte[] myArray = new byte[10000];
    }
}

但使用命令行運行它:

java.exe myfirstjavaprog -Xms 100 -Xmx 100

這樣,Java最多只能分配100個字節的內存。 由於10,000字節的數組無法放入100字節的堆中,因此即使操作系統具有大量RAM,也會拋出OutOfMemory異常。

我希望這是有道理的...


編輯:

回到“為什么Java使用這么多內存”; 您為什么認為它占用大量內存? 如果您正在查看操作系統報告的內容,那么這並不是其實際使用的內容,而是僅保留使用的內容。 如果您想知道Java實際使用了什么,則可以進行堆轉儲並瀏覽堆中的每個對象,並查看其使用了多少內存。

要回答“為什么不讓OS處理呢?”,我想對於那些設計它的人來說,這只是一個基本的Java問題。 我的看法 Java在JVM(虛擬機)中運行。 如果創建VMWare實例或系統的幾乎任何其他“虛擬化”,通常必須指定虛擬系統將/將消耗多少內存。 我認為JVM是相似的。 同樣,這種抽象的內存模型使不同操作系統的JVM都以類似的方式起作用。 因此,例如,Linux和Windows具有不同的RAM分配模型,但是JVM可以將其抽象化並遵循不同操作系統的相同內存使用情況。

Java確實使用了mallocfree ,或者至少使用了JVM的實現。 但是,由於Java跟蹤分配並且垃圾收集了無法訪問的對象,所以絕對不夠。

至於其余的文字,我不確定那里是否有問題。

即使是一些非常小的示例演示應用程序也會加載大量的內存。 也許是由於Java庫已加載。 但是為什么需要為每個Java實例加載庫? (之所以這樣,是因為多個小型應用程序線性地占用了更多的內存。有關我描述此問題的詳細信息,請參見此處。)或者為什么這樣做呢?

這可能是由於啟動和運行JVM的開銷所致

像Eclipse這樣的大型Java應用程序經常會因某些OutOfMemory異常而崩潰。 這總是很奇怪,因為我的系統上仍然有足夠的可用內存。 通常,它們在運行時會消耗越來越多的內存。 我不確定它們是否存在某些內存泄漏,或者是由於內存池中的碎片造成的,我感覺后者就是這種情況。

我不確定您所說的“經常崩潰”是什么意思,因為我認為這種情況在很長一段時間內都沒有發生過。 如果是這樣,則可能是由於您之前提到的“最大尺寸”設置所致。

您的主要問題是,為什么Java不使用mallocfree取決於目標市場。 Java旨在消除開發人員的內存管理難題。 Java的垃圾回收器在可以釋放內存時在釋放內存方面做得相當不錯,但是在內存受限的情況下,Java並不打算與C ++競爭。 Java很好地完成了預期的工作(刪除了開發人員級別的內存管理),並且JVM很好地承擔了責任,以至於對於大多數應用程序來說已經足夠好了。

這些限制是Sun故意設計的決定。 我已經看到至少另外兩個沒有這種設計的JVM-Microsoft的和IBM的非pc AS / 400系統。 兩者都根據需要使用所需的更多內存來增長。

Java不使用固定大小的內存,而是始終在-Xms到-Xmx的范圍內。

如果Eclipse因OutOfMemoryError而崩潰,則它所需的內存將超過-Xmx所授予的內存(配置問題)。

Java不得使用malloc / free(用於創建對象),因為由於垃圾回收(GC),其內存處理方式有很大不同。 GC自動刪除未使用的對象,與負責內存管理相比,這是一個好處。

有關此復雜主題的詳細信息,請參見優化垃圾收集。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM