java 內存優化

一.代碼優化

  內存會溢出肯定和代碼逃不了關係,99.99%學java的人都知道垃圾回收器是 java的一大優點並據此來嘲笑C++。顯然這個特性爲代碼編寫者省了不少事,但這個特性卻帶來了不少隱患。舉個例子在遊戲當中經常有不同場景的切換,如從遊戲邏輯退到主菜單邏輯,對遊戲邏輯對象的態度很多人會選擇忘記等待垃圾回收器來收屍。乍看之下似乎並無不妥垃圾回收器會來善後。實際上垃圾回收器並非實時的,它不像C++的Delete語句馬上釋放不用的內存。當從遊戲邏輯切換到主菜單邏輯這時兩個對象同時存在很可能這時內存就不夠用了。讀到這裏很多人會發現實際上垃圾回收器在j2me上並不怎麼好用,從一個角度上來講在j2me上所有垃圾必須由手工釋放,除簡單類型以外所有對象都必須顯式地置空例如 imgs=null; 實際上java提供了一個不錯的工具用來查找內存溢出,java.lang.Runtime.freeMemory() 。它可以返回當前的剩餘內存數,將它適當的安放在代碼中可以有效的監測內存使用狀況。很大一部份的j2me程序員之前都是從事pc軟件開發工作,充裕的內存掩蓋了許多寫代碼的不良習慣。如下所示:

         //a 不爲空
  a=new Logic();
 
  很多人可能對此有異議,他們會認爲新的對象會把舊的對象沖掉並且釋放內存。這裏麪包含兩個問題:1. 該段代碼是先創建對象然後再進行賦值操作的,也就是說在這期間有兩個對象同時存在這就很可能會產生溢出。2. 這樣做也會妨礙垃圾回收器的工作

  較好的寫法如下:

         a=null;
  a=new Logic();
 


  雖然麻煩了點但在j2me中還是必要的。接着看下例。

  drawString("遊戲時間:" + time ,50,50,Graphics.LEFT|Graphics.TOP);

  "遊戲時間:" + time 很完美在paint()方法當中每次都被刷一遍顯示在屏幕上。危機往往隱藏在美麗的外表,該語句會引起新的內存重新分配來存儲 "遊戲時間:" + time 而顯示完以後又必須由垃圾回收器釋放,用了雙倍時間,並且容易發生內存溢出。依此類推在重複執行的方法裏應儘量避免重複定義對象。與paint()方法類似在循環裏也有類似的情況存在。

  把所有對象的初始化放在構造函數裏想必是再正當不過了,大多數人通常的做法是把當前邏輯所要用到的資源通通初始化完畢。

  很大一部份的內存溢出都是發生在構造函數中。內存使用的高峯期都是在構造函數中所以避開這個高峯能有效的防止溢出。建議最好的辦法是第一次使用時初始化。如下所示

         if (img==null){
  //初始化

  }
 
  現在做遊戲很多時候都需要地圖數組,聲音數組,還有一些其它資源這些資源很多可以放在代碼中也有的可以放在文件當中。

  強烈建議將這些資源放在文件中需要時在load進來。這些資源文件如果放在代碼中則會佔用不小的代碼段空間,而代碼一般是程序一運行就裝載到內存當中。

  除上面列舉的方法外還有一些大家所熟知的順便一提, 比如關閉沒用的rms ,關閉沒用的網絡連接,關閉沒用的流。正確地停止線程。良好的程序架構減少代碼偶合性也是一個不錯的方法,無論在代碼調式,內存釋放都可以做到非常清析。

  二.圖片優化

  j2me的內存殺手無疑非圖片莫屬,一張3k的圖片可以佔用20多k的內存不信大家把load前後的內存剩餘打印出來對比看看。所以防止內存溢出最直接的辦法就是從圖片入手。

  1.圖片壓縮: 多數人馬上會想到這個辦法。不錯這個辦法是最有效的。在photoshop裏圖片製作完成後不要選擇 "存儲爲",而是選擇 "存儲爲 web 所用格式" 可以根據裏面的選項進行壓縮,特別是顏色這一項越小越好不過相應的圖像會有所失真。不要認爲這樣就完了。

  實際上該圖片還可以再次壓縮,在網上有許多類似的工具。推薦一款可以壓縮png格式的軟件 xat.com Image Optimizer 效果不錯。經常都有 70% 的壓縮率且圖像不會失真。

  假如你有多張規格一樣的圖片,那麼建議你把它做成一張長條圖片。有兩個原因:

  1、 這樣節省存儲空間和內存空間。大家可做個試驗將10張圖片的內容放在一張當中對比看看文件大小有沒有變化。

  2 、10張圖片需要10個image 對象需要進行10次io操作浪費時間不說還浪費內存。當筆者發現這個好處時興奮地把所有圖片都存成一張,吱地一聲內存又溢出了...原因想必大家也知道!!圖片太大了不要把不同界面的圖片整合在一起否則經常會得不償失。

  作圖時還有一些細節需要注意,顏色數量,分辯率,圖像模式(最好是索引顏色),畫布大小都會影響到圖片大小。

  三.工具優化

  誰都知道混淆器是用來保護代碼的以加大反編譯的難度(個人認爲這是在嘲笑程序員的智商)。實際上用它來優化程序也是不錯的選擇,至少有兩點好處:

  1、 壓縮程序大小。一個60k的程序經常可以壓掉10k左右。10k的空間對於寫低端手機的程序員簡直是雪中送碳,多少超過64k限制的遊戲都受過它的恩惠;

  2、節省內存空間。用腳去想也想得出來代碼少了內存裏的代碼段自然就短了。

本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/yuxinyu999/archive/2010/03/06/5352807.aspx

 

#########################

最近在網上看到一些人討論到java.lang.Runtime類中的freeMemory(), totalMemory(), maxMemory()這幾個方法的一些問題,很多人感到很疑惑,爲什麼,在java程序剛剛啓動起來的時候freeMemory()這個方法返回的只有 一兩兆字節,而隨着java程序往前運行,創建了不少的對象,freeMemory()這個方法的返回有時候不但沒有減少,反而會增加。這些人對 freeMemory()這個方法的意義應該有一些誤解,他們認爲這個方法返回的是操作系統的剩餘可用內存,其實根本就不是這樣的。這三個方法反映的都是 java這個進程的內存情況,跟操作系統的內存根本沒有關係。下面結合totalMemory(), maxMemory()一起來解釋。

maxMemory()這個方法返回的是java虛擬機(這個進程)能構從操作系統那裏挖到的最大的內存,以字節爲單位,如果在運行java程序的時 候,沒有添加-Xmx參數,那麼就是64兆,也就是說maxMemory()返回的大約是64*1024*1024字節,這是java虛擬機默認情況下能 從操作系統那裏挖到的最大的內存。如果添加了-Xmx參數,將以這個參數後面的值爲準,例如java -cp you_classpath -Xmx512m your_class,那麼最大內存就是512*1024*1024字節。

totalMemory()這個方法返回的是java虛擬機現在已經從操作系統那裏挖過來的內存大小,也就是java虛擬機這個進程當時所佔用的所有 內存。如果在運行java的時候沒有添加-Xms參數,那麼,在java程序運行的過程的,內存總是慢慢的從操作系統那裏挖的,基本上是用多少挖多少,直 到挖到maxMemory()爲止,所以totalMemory()是慢慢增大的。如果用了-Xms參數,程序在啓動的時候就會無條件的從操作系統中挖 -Xms後面定義的內存數,然後在這些內存用的差不多的時候,再去挖。

freeMemory()是什麼呢,剛纔講到如果在運行java的時候沒有添加-Xms參數,那麼,在java程序運行的過程的,內存總是慢慢的從操 作系統那裏挖的,基本上是用多少挖多少,但是java虛擬機100%的情況下是會稍微多挖一點的,這些挖過來而又沒有用上的內存,實際上就是 freeMemory(),所以freeMemory()的值一般情況下都是很小的,但是如果你在運行java程序的時候使用了-Xms,這個時候因爲程 序在啓動的時候就會無條件的從操作系統中挖-Xms後面定義的內存數,這個時候,挖過來的內存可能大部分沒用上,所以這個時候freeMemory()可 能會有些大。

#############################

從目前應用啓動腳看沒有人爲添加內存分配的參數,所以系統以默認64M來進行分配的,如果應該對內存要求超過64M就會導制內存溢出,所以,可以根據應用的實際使用內存情況來進行分析,找出最大值和最小值,或以歷史估算一個使用參數對啓動參數進行修改;

java -Xms512m -Xmx512m com.mochasoft.sm.CmppSmSender

重啓應用再次觀察

 

 

1.別用new Boolean
  在很多場景中Boolean類型是必須的,比如JDBC中boolean類型的set與get都是通過Boolean封裝傳遞的,大部分ORM也是用Boolean來封裝boolean類型的,比如:
       ps.setBoolean("isClosed",new Boolean(true));
       ps.setBoolean("isClosed",new Boolean(isClosed));
       ps.setBoolean("isClosed",new Boolean(i==3));
  通常這些系統中構造的Boolean實例的個數是相當多的,所以系統中充滿了大量Boolean實例小對象,這是相當消耗內存的。Boolean類實際上只要兩個實例就夠了,一個true的實例,一個false的實例。

  Boolean類提供兩了個靜態變量:
        public static final Boolean TRUE = new Boolean(true);
        public static final Boolean FALSE = new Boolean(false);

  需要的時候只要取這兩個變量就可以了,比如:
        ps.setBoolean("isClosed",Boolean.TRUE);
  那麼象2、3句那樣要根據一個boolean變量來創建一個Boolean怎麼辦呢?可以使用Boolean提供的靜態方法: Boolean.valueOf()  比如:
        ps.setBoolean("isClosed",Boolean.valueOf(isClosed));
        ps.setBoolean("isClosed",Boolean.valueOf(i==3));
  因爲valueOf的內部實現是:return (b ? TRUE : FALSE);
  所以可以節省大量內存。相信如果Java規範直接把Boolean的構造函數規定成private,就再也不會出現這種情況了。

2.別用new Integer
  和Boolean類似,java開發中使用Integer封裝int的場合也非常多,並且通常用int表示的數值通常都非常小。SUN SDK中對Integer的實例化進行了優化,Integer類緩存了-128到127這256個狀態的Integer,如果使用 Integer.valueOf(int i),傳入的int範圍正好在此內,就返回靜態實例。這樣如果我們使用Integer.valueOf代替new Integer的話也將大大降低內存的佔用。如果您的系統要在不同的SDK(比如IBM SDK)中使用的話,那麼可以自己做了工具類封裝一下,比如IntegerUtils.valueOf(),這樣就可以在任何SDK中都可以使用這種特性。

3.用StringBuffer代替字符串相加

  這個我就不多講了,因爲已經被人講過N次了。我只想將一個不是笑話的笑話,我在看國內某“著名”java開發的WEB系統的源碼中,竟然發現其中大量的使用字符串相加,一個拼裝SQL語句的方法中竟然最多構造了將近100個string實例。無語中!

4.過濫使用哈希表

  有一定開發經驗的開發人員經常會使用hash表(hash表在JDK中的一個實現就是HashMap)來緩存一些數據,從而提高系統的運行速度。比如使用HashMap緩存一些物料信息、人員信息等基礎資料,這在提高系統速度的同時也加大了系統的內存佔用,特別是當緩存的資料比較多的時候。其實我們可以使用

  操作系統中的緩存的概念來解決這個問題,也就是給被緩存的分配一個一定大小的緩存容器,按照一定的算法淘汰不需要繼續緩存的對象,這樣一方面會因爲進行了對象緩存而提高了系統的運行效率,同時由於緩存容器不是無限制擴大,從而也減少了系統的內存佔用。現在有很多開源的緩存實現項目,比如 ehcache、oscache等,這些項目都實現了FIFO、MRU等常見的緩存算法。

5.避免過深的類層次結構和過深的方法調用

  因爲這兩者都是非常佔用內存的(特別是方法調用更是堆棧空間的消耗大戶。

6.變量只有在用到它的時候才定義和實例化

7.儘量避免使用static變量,類內私有常量可以用final來代替
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章