在Java性能優化系列中,內存管理是一個要優先考慮的關鍵因素。而說到內存分配,就必然會涉及到 基本類型和引用類型。所以我們今天就先來介紹一下這兩種類型在性能方面各自有什麼奧妙(關於這兩 種類型在其它方面的奧妙,我會另外寫一個帖子)。
★名詞定義
先明確一下什麼是基本類型,什麼是引用類型。簡單地說,所謂基本類型就是Java語言中如下的8種 內置類型:boolean、char、byte、short、int、long、float、double。而引用類型就是那些可以通過 new來創建對象的類型(基本上都是派生自Object)。
★兩種類型的存儲方式
這兩種類型的差異,首先體現在存儲方式上。在Java中,引用類型是存儲在堆(Heap)上的;而基本 類型是存儲在棧(Stack)上。可能有同學會小聲問:堆和棧有啥區別捏?要說堆和棧的差別,那可就大 了去了。如果你對這兩個概念還是不太明白或者經常混淆,建議先找本操作系統的書拜讀一下。
★堆和棧的性能差異
堆和棧在性能方面是有很大差別滴。堆相對進程來說是全局的,能夠被所有線程訪問;而棧是線程局 部的,只能本線程訪問。打個比方,棧就好比個人小金庫,堆就好比國庫。你從個人小金庫拿錢去花, 不需要辦什麼手續,拿了就花,但是錢數有限;而國庫裡面的錢雖然很多,但是每次申請花錢要打報告 、蓋圖章、辦N多手續,耗時又費力。
同樣道理,由於堆是所有線程共有的,從堆裡面申請內存要進行相關的加鎖操作,因此申請堆內存的 復雜度和時間開銷比棧要大很多;從棧裡面申請內存,雖然又簡單又快,但是棧的大小有限,分配不了 太多內存。
★為什麼這樣設計?
可能有同學又問了,干嘛把兩種類型分開存儲,干嘛不放到一起捏?這個問題問得好!下面我們就來 揣測一下,當初Java為啥設計成這樣。
當年Java它爹(James Gosling)設計語言的時候,對於這個問題有點進退兩難。如果把各種東東都 放置到棧中,顯然不現實,一來棧是線程私有的(不便於共享),二來棧的大小是有限的,三來棧的結 構也間接限制了它的用途。那為啥不把各種東東都放置到堆裡面捏?都放堆裡面,倒是能繞過上述問題 ,但是剛才也提到了,申請堆內存要辦很多手續,太繁瑣。如果僅僅在函數中寫一個簡單的“int n = 0”,也要到堆裡面去分配內存,那性能就大大滴差了(要知道Java是1995年生出來的,那年 頭我家的PC配4兆內存就屬豪華配置了)。
左思右想之後,Java它爹只好做了一個折中:把類型分為基本類型和引用類型;引用類型(Object派 生)的對象存放到堆裡面;把基本類型(非Object派生)的值存放到棧裡面。所以,你從Java語法上也 可以看出兩者的差別:引用類型可以用new創建對象(對於某些單鍵,表面上沒用new,但是在 getInstance()內部也還是用的new);而基本類型則不需要用new來創建。
★這樣設計的弊端
順便跑題一下,斗膽評價Java它爹這種設計的弊端(希望Java Fans不要跟我急)。我個人認為:這 個折中的決策,帶來了許多深遠的影響,隨手舉出幾個例子:
1、由於基本類型不是派生自Object,因此不能算是純種的對象。這導致了Java的“純面向對象 ”招牌打了折扣。
2、由於基本類型不是派生自Object,出於某些場合(比如容器類)的考慮,不得不為每個基本類型 加上對應的包裝類(比如Integer、Byte等),使得語言變得有點冗余。
★結論
從上述的介紹,我們應該明白,使用new創建對象的開銷是不小的。在程序中能避免就應該盡量避免 。另外,使用new創建對象,不光是創建時開銷大,將來垃圾回收時,銷毀對象也是有開銷的(關於GC的 開銷,咱們會在後面的帖子細談)。下一個帖子,我們找一個例子來實戰一下。
本文原始地址:
http://program-think.blogspot.com/2009/03/java-performance-tuning-1-two-types.html