應該使用32位還是64位的JVM?
應用使用32位的JVM,還是應該使用64位的JVM,我自己以前還真沒想過,大家都想過了嗎?本文是Plumbr中的一篇博文,為大家提了個醒。(2012.11.27最后更新)在我的企業級軟件開發職業生涯中,我多次面對這個問題。每隔一段時間我不得不建議配置一個新的特定環境。而往往該問題部分與"我應該使用32位還是64位JVM?"這一問題有關。老實說,一開始我是靠擲硬幣來解決的,而不是給出一個合理的答案。(抱歉,兄弟們!)但現在我對這個問題有了更多的領悟,并想與你們分享。
第一步--越多越好,對嗎?即如此,因為64>32,所以答案很簡單:如果可能的話,應該總是選擇64位?好吧,請耐心點兒。64位架構的壞處是,相同的數據結構會消耗更多的內存,甚至是多很多。我們的測評顯示,根據不同的JVM版本和操作系統版本,以及相應的硬件架構,最終會用掉比32位架構多30-50%的堆內存。更大的堆也會造成更長的GC暫停,這會對應用程序造成潛在的影響--在4.5G的堆上執行完全GC肯定會比在3G的堆上執行花費更長的時間。所以,僅僅是因為64比32大就去趕64位JVM的時髦,肯定是錯誤的。
但,什么時候才是使用64位JVM的好時機呢?多數情況下,要看堆的大小。在不同的平臺下,你很快就得面對32位JVM堆內存的上限問題。下表列出了在不同平臺下的這種限制:
Linux 2GB 特定內核,如hugemem,可以達到3G
Windows 1.5GB 使用"/3G"的啟動參數并使用/LARGEADDRESSAWARE參數去編譯JRE,則可提高到3G
Mac OS X 3.8GB 警告--未能找到老的Mac,所以沒有對其進行測試
那么,這有什么壞處?我打賭,你肯定見過在16G的RAM上運行32位的機器。問題就在于,在只有16G RAM的Windows系統中,JVM只能分配到少于10%的內存。
主因--地址空間。在32位系統中,理論上可以為每個進程分配4G內存。而Windows系統對地址空間的處理使這一理論值無法達到。Windows將進程的地址空間砍掉了一半。一半留給了內核(用戶進程無法使用它),另一半則留給了用戶。無論系統中有多大的RAM,32位進程只能使用到2G的RAM。更糟的是--地址空間必須是連續的,所以在實踐中,Windows系統最多只為你剩下了1.5-1.8G的堆內存。
有一個在32位Windows系統中減少內核空間并增加用戶空間的竅門,即,可以在boot.ini系統使用/3GB參數。然而,為了能確保它有效,必須使用/LARGEADDRESSAWARE開關去編譯或鏈接JVM。
不幸地是,至少對于HotSpot JVM無法做到這一點。直到最新的JDK 1.7版本,HotSpot JVM仍未使用該選項進行編譯。但又幸運地是,如果你運行2006年之后的JRockit版本,就能享用到2.8-2.9G的堆大小。
那么,我們是否可以得出結論,如果你的應用要求大于2-3G的內存,你就總是應該運行64位的JVM?也許。但你也必須要清楚應用的場景。我們已經介紹了使用64位JVM的壞處--增加的堆消耗,以及更長的GC中斷。讓我們分析一下原因。
問題1:64位JVM需要多出30-50%的內存。為什么會如此呢?主要是因為內存是以64位架構進行部局。首先,在64位JVM中,對象頭有12字節。其次,對象引用會占用4字節或8字節,實際值取決于JVM的參數與堆的大小。相較于32位JVM的8字節對象頭和4字節對象引用,毫無疑問會增加一些開銷。在我們之前發布的博客中你會找到更多關于如何計算對象內存的相關信息。
問題2:更長的垃圾收集中斷。構建更大的堆意味著GC要做更多的工作去清理無用的對象。這意味著,在實際應用中構建大于12-16G的堆時,你必須要特別小心。沒有很好的性能調優與測評,你很容易就會引起一個耗時數分鐘的完全GC。在應用程序的非關鍵潛在危險場景中,通過對吞吐量的優化或許能解決這一問題,但在多數情況下,它會造成程序中斷。
當我需要更大的內存且又不希望引入由64位架構所造成的開銷,那該怎么辦呢?在我們以前的一篇博文中已經涉及了這個問題--通過對堆的分區,GC調優,構建不同的JVM,或對堆分配不同的內存,就可以避免這一問題。
最后,讓我們重申,你應該總是要意識到選擇64位JVM的后果,但也不要懼怕這一選擇。