Java 記憶體配置及容量擴充__Java

來源:互聯網
上載者:User

一、Java 進程的記憶體使用量

當運行一個Java應用程式時,Java 運行時會建立一個作業系統進程,作為作業系統進程,Java 運行時面臨著與其他進程完全相同的記憶體限制 架構提供的記憶體定址能力依賴於處理器的位元,舉例來說,32位或者64位進程能夠處理的位元決定了處理器能定址的記憶體範圍:32位提供了 2^32 的可定址範圍,也就是 4,294,967,296 位,或者說 4GB。而 64 位元處理器的可定址範圍明顯增大:2^64,也就是 18,446,744,073,709,551,616,或者說 16 exabyte(Exabyte)。
OS和C運行時佔用的記憶體數量取決於所用的OS,但通常數量較大:Windows 預設佔用的記憶體是2GB。剩餘的可定址空間就是可供啟動並執行實際進程使用的記憶體。

對於Java應用程式,使用者空間是 Java 進程佔用的記憶體,實際上包含兩個池:Java 堆和本機(非 Java)堆。Java 堆的大小由 JVM 的Java堆設定控制:-Xms 和 -Xmx 分別設定最小和最大 Java 堆。在按照最大的大小設定分配了Java堆之後,剩下的使用者空間就是本機堆。下面展示了一個 32 位 Java 進程的記憶體布局

可定址範圍總共有 4GB,OS 和 C 運行時大約佔用了其中的 1GB,Java 堆佔用了將近 2GB,本機堆佔用了其他部分。請注意,JVM 本身也要佔用記憶體,就像 OS 核心和 C 運行時一樣,而 JVM 佔用的記憶體是本機堆的子集。

二、Java 對象記憶體使用量情況

Java代碼使用new操作符建立一個Java對象的執行個體時,實際上分配的資料要比您想的多得多。例如,一個int值與一個Integer對象的大小比率是 1:4.額外的開銷源於 JVM 用於描述 Java 對象的中繼資料.其中通常包括:
類:一個指向類資訊的指標,描述了物件類型。舉例來說,對於 java.lang.Integer 對象,這是 java.lang.Integer 類的一個指標。
標記:一組標記,描述了對象的狀態,包括對象的散列碼(如果有),以及對象的形狀(也就是說,對象是否是數組)。
鎖:對象的同步資訊,也就是說,對象目前是否正在同步。
對象中繼資料後緊跟著對象資料本身,包括對象執行個體中儲存的欄位。對於 java.lang.Integer 對象,這就是一個 int。
如果您正在運行一個 32 位 JVM,那麼在建立 java.lang.Integer 對象執行個體時,對象的布局可能如圖

 

有128位的資料被佔用,其中用於儲存int值的為32位,而對象中繼資料佔用了其餘的96位。

三、Java 數組對象記憶體使用量情況

數組對象(例如一個 int 值數組)的形狀和結構與標準 Java 對象相似。主要差別在於數組對象包含說明數組大小的額外中繼資料。
因此,資料對象的中繼資料套件括:
類:一個指向類資訊的指標,描述了物件類型。舉例來說,對於 int 欄位數組,這是 int[] 類的一個指標。
標記:一組標記,描述了對象的狀態,包括對象的散列碼(如果有),以及對象的形狀(也就是說,對象是否是數組)。
鎖:對象的同步資訊,也就是說,對象目前是否正在同步。
大小:數組的大小。
int 數組對象的布局樣本

 

有160位的資料用於儲存int值內的32位元據,而數組中繼資料佔用了其餘160位。

四、比較32位和64位Java對象記憶體使用量情況
64 位元處理器的記憶體可定址能力比 32 位處理器高得多。對於 64 位元進程,Java 對象中的某些資料欄位的大小(特別是對象中繼資料)也需要增加到 64 位元。其他資料欄位類型(例如 int、byte 和 long )的大小不會更改。
一個 64 位元進程的 java.lang.Integer 對象和 int 數組的布局樣本

 

對於一個 64 位元 Integer 對象,現在有 224 位的資料用於儲存 int 欄位所用的 32 位,開銷比例是 7:1。對於一個 64 位元單元素 int 數組,有 288 位的資料用於儲存 32 位 int 條目,開銷比例是 9:1。在 32 位 Java 運行時中啟動並執行應用程式若遷移到 64 位元 Java 運行時,其 Java 堆記憶體使用量量會顯著增加。通常情況下,增加的數量是原始堆大小的 70% 左右。
,一個在 32 位 Java 運行時中使用 1GB Java 堆的 Java 應用程式在遷移到 64 位元 Java 運行時之後,通常需要使用 1.7GB 的 Java 堆。請注意,這種記憶體增加並非僅限於 Java 堆。本機堆記憶體區使用量也會增加,有時甚至要增加 90% 之多。

五、Java 集合的記憶體使用量

大量資料都是使用核心 Java API 提供的標準 Java Collections 類來儲存和管理的。非常有必要瞭解各集合提供的功能以及相關的記憶體開銷。總體而言,集合功能的層級越高,記憶體開銷就越高,
其中部分最常用的集合如下:
HashSet
HashMap
Hashtable
LinkedList
ArrayList
(HashSet 是包圍一個 HashMap 對象的封裝器,它提供的功能比 HashMap 少,同時容量稍微小一些。)

屬性匯總,記憶體開銷

集合 效能 預設容量 空時的大小 10K 條目的開銷 準確設定大小。 擴充演算法
HashSet O(1) 16 144 360K x2
HashMap O(1) 16 128 360K x2
Hashtable O(1) 11 104 360K x2+1
LinkedList O(n) 1 48 240K +1
ArrayList O(n) 10 88 40K x1.5
StringBuffer O(1) 16 72 24 x2
Hash 集合的效能比任何 List 的效能都要高,但每條目的成本也要更高。對於並不那麼注重訪問效能的較小集合而言,List 則是合理的選擇。ArrayList 和 LinkedList 集合的效能大體相同,但其記憶體佔用完全不同:ArrayList 的每條目大小要比 LinkedList 小得多,但它不是準確設定大小的。List 要使用的正確實現是 ArrayList 還是 LinkedList 取決於 List 長度的可預測性。如果長度未知,那麼正確的選擇可能是 LinkedList,因為集合包含的空白空間更少。如果大小已知,那麼 ArrayList 的記憶體開銷會更低一些。

六、ArrayList和HashMap容量增長方式

(1)、ArrayList 容量增長的方式

public boolean add(E e) {   ensureCapacity(size + 1); // 容量增長  elementData[size++] = e;   return true;   }public void ensureCapacity(int minCapacity) {   modCount++;   int ldCapacity = elementData.length;   if (minCapacity > oldCapacity) {   Object oldData[] = elementData;   int newCapacity = (oldCapacity * 3)/2 + 1;   if (newCapacity < minCapacity) {  newCapacity = minCapacity; }    elementData = Arrays.copyOf(elementData, newCapacity); //依次拷貝  } } 


int newCapacity = (oldCapacity * 3)/2 + 1;  也就是原有容量的1.5倍+1。然後通過底層的複製方法將原有資料複製過來
如果資料量很大,那麼造成數組重新分配的次數會增加,但對於一般的資料量下,
1千需要分配 11次
1萬一級需要分配17次
10萬 需要分配23次
100萬需要分配28次
所以,大家根據實際情況,大致分配一個初始化的容量還是有必要的。但是如果你初始容量太大,而資料增長很慢,那麼就在浪費記憶體了。
如何取捨,還是看具體的應用情境了。


(2)、HashMap 容量增長方式
HashMap主要是用數組來儲存資料的,我們都知道它會對key進行雜湊運算,哈系運算會有重複的雜湊值,對於雜湊值的衝突,HashMap採用鏈表來解決的。
transient Entry[] table;
Entry 就是HashMap儲存資料所用的類,它擁有的屬性如下
final K key;
V value;
final int hash; 
Entry<K,V> next; //next就是為了雜湊衝突而存在的。


當put一個元素時,如果達到了容量限制,HashMap就會擴容,新的容量永遠是原來的2倍。
if (size++ >= threshold){
   resize(2 * table.length);
}
達到 threshold指定的值時就開始擴容, threshold=最大容量*載入因子。
如果資料大小是固定的,那麼最好給HashMap設定一個合理的容量值
根據上面的分析,HashMap的初始預設容量是16,預設載入因子是0.75,也就是說,如果採用HashMap的預設建構函式,當增加資料時,資料實際容量超過16*0.75=12時,HashMap就擴容,擴容帶來一系列的運算,建立一個是原來容量2倍的數組,對原有元素全部重新雜湊,如果你的資料有幾千幾萬個,而用預設的HashMap建構函式,那結果是非常悲劇的,因為HashMap不斷擴容,不斷雜湊,在使用HashMap的情境裡,不會是多個線程共用一個HashMap,除非對HashMap封裝並同步,由此產生的記憶體開銷和cpu開銷在某些情況下可能是致命的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.