java是一門面向對象語言。在java里,除了8種基本類型外,其他的都是對象。java的對象存放在堆(heap)上。每當你new一個對象,就會在堆上搶占一定的內存。java虛擬機的垃圾收集器(gc)一直在后臺工作,當它確定某些對象不再被使用時,就會銷毀它,回收堆上的內存。java垃圾收集器不是使用單純的引用計數來確定一個對象是否已不再被使用,而是使用一種相對復雜的算法。這種算法也考量引用計數,但是還考量其他一些東西。據說這種算法能夠判斷出循環引用(就是A引用B,B引用C,C再引用A)。總之SUN是鼓吹這種算法要比其他算法優秀。我相信這種算法能夠提高程序的穩定性。不過這種算法的一個很大的問題是內存回收效率很差。java的一道經典考題是這樣的:
for (int i=0;i<10;i++) {
??? ClassA a = new ClassA();
}
問執行完以后,內存里有幾個ClassA的對象實例。答案是10個。為什么,因為根據java神秘的垃圾回收算法,這些超出作用域的,不再有人引用的對象實例并不急著銷毀。這在平時不會造成什么問題,但是如果在循環里創建了占用很大內存的對象,不能及時回收就會造成堆內存耗盡,溢出。java提供了System.gc()來讓你建議虛擬機回收內存,但僅僅是建議,不是強制。
java虛擬機啟動的時候會先向操作系統請求一小塊內存作為堆內存。隨著在堆里存放的數據(對象)越來越多,占用內存超過一定百分比的時候,java虛擬機就會向操作系統請求更多的內存分配給堆。我們在程序里可以使用Runtime.getRuntime().freeMemory()和Runtime.getRuntime().totalMemory()來獲得當前堆的剩余內存和總內存。java虛擬機向操作系統請求的內存是不會歸還的。還好它不會無限制地請求內存,有一個上限值。在jre1.5里,這個上限值缺省是64M。你可以通過虛擬機參數 -Xmx 來設置這個值。比如下面我設置這個值為640M:
java -Xmx640m org.formalin14.SampleClass
最近我在做批處理大量xml的時候,在每個循環里處理一個文件。每個循環里都要做很多運算和轉換,同時也需要創建很多對象來保存中間數據。程序總是在處理了1000多個文件以后就報java.lang.OutOfMemoryError錯誤,是堆內存不夠用了。我檢查程序并確保循環里每個創建的對象在用完后都置為null(其實沒有必要),并且優化了算法和程序結構,還在每次循環末尾加上System.gc()。但是在循環里總是不可避免地要創建新對象。而且,對于java虛擬機,你沒有任何辦法強制它銷毀對象回收內存。這樣,64M的堆內存絕對是不夠它搗騰的。沒辦法只好把堆上限改到640M,總算夠它搗騰了。
總的來說,平時還是應該養成良好的編程習慣,盡量節約內存和CPU,建議把堆上限調到8M,強制自己寫高質量的代碼。其他一些技巧比如避免讀取整個文件到內存,讀一點處理一點,然后就扔掉(祈禱扔掉以后能被趕快回收)。用SAX處理xml。用StringBuilder構造字符串。多用大量的數據做測試,能看出程序是否真的優秀。對于執行快的循環,末尾要加Thread.sleep(1),不然CPU一下就上去了。我前面的這個程序還是優化得不夠,如果真有時間的話,相信任何程序都是可以優化得很好的。不過不管怎么努力,java程序的效率怎么也比不上用c寫的程序。