程序員羽化之路--假如需要一百萬個對象

image

設計背景

每個平臺都會有用戶這種基礎數據的設計,作爲最基礎的用戶,每個用戶都有很多屬性,比如性別,姓名,手機號等,每個用戶還可以有類似經驗值這樣的榮譽系統,根據不同的經驗值來對應不同的等級,不同的等級對應不同的榮譽UI,比如一級用戶可能只顯示一個星星,二級用戶顯示兩顆星星,以此類推,類似於QQ等級的星星月亮太陽,這樣的榮譽系統隨着平臺的不斷壯大,可能會衍生出很多類型。那麼問題來了,用戶登錄的時候就需要初始化用戶的這些榮譽值,以星星數爲例,類似於以下代碼


public class Star
{
    //等級
    public int Level{get ;set ;}
    //對應的星星數目
    public int StarNumber{get ;set ;}
    //對應的星星顏色
    public int Color{get ;set ;}

    ... 其他屬性
}
//用戶信息
public class User{   
    public Star StarInfo{get ;set ;}
    //...用戶的其他屬性
}

//初始化用戶信息
User u=new User(){ StarInfo=new Star(){ Level=1, StarNumber=1,Color=1}};

每一個登錄用戶都會初始化一個Star屬性來表示當前用戶的Star信息,當有100萬用戶甚至更多用戶同時在線的時候,內存中就實例化了同樣數量的Star對象,以及其他類似的屬性對象。這麼多重複的對象難道不能優化嗎?當然不是!!

問題分析

一個業務出現問題,首先要分析問題的所在。根據以上所說,問題的根本在於產生了大量的對象,首先每個用戶對象都有自己獨特的狀態,這個基本上不可能分解優化,但是類似Star這樣的屬性就有優化途徑了,這些榮譽屬性一個最大的共同點就是不可變,換句話說,等級1的用戶對應的Star信息是永遠不會變的,永遠是level=1,starnumber=1,color=1 等。基於這個不變性,我們可以把這個Star抽離出來,供所有等級1的用戶使用,假設原來有10萬等級1的用戶,原來需要10萬個對象,現在只需要一個對象,這可是天壤之別。

解決問題

基於以上問題分析,我們需要做的是把對象重複使用,只要是對象重複問題,基本上可以利用一個對象出口來解決問題,類似於以下的對象初始化工廠,但是要注意線程安全問題,因爲同時請求並初始化對象的線程會有多個。

    public class UserStarFac
    {
        static object objLock = new object();
        static Dictionary<int, Star> UserStarMap = new Dictionary<int, Star>();
        public static Star GetUserStar(int level)
        {
            //利用鎖來防止實例化多次,當然這裏可以優化
            lock (objLock)
            {
                Star info = null; ;
                if(!UserStarMap.TryGetValue(level, out info))
                {
                    info = new Star() { Color = 1, Level = 1, StarNumber = 1 };
                    UserStarMap.Add(level,info);
                }
                return info;
            }
        }
    }

編寫簡單測試程序

 static void Main(string[] args)
        {
            int i = 0;
            List<User> userList = new List<User>();
            while (i < 100000)
            {
               // userList.Add(new User() {  StarInfo=new Star() {  Color=1, Level=1, StarNumber=1} });
                userList.Add(new User() {  StarInfo= UserStarFac .GetUserStar(1)});
                i++; 
            }
            Console.WriteLine("初始化完成");
            Console.Read();
        }

內存的測試結果:

不執行任何程序:佔用內存:2.8 M

無優化初始化10萬對象:佔用內存:11 M

優化之後初始化10萬對象:佔用內存:7 M

居然一個小小的優化就減少了4M內存,不要小看這小小的4M,你要看的是比例,居然減少了將近 50%,真實業務中,可以進行這種優化的地方數不勝數,不知道你是否在乎呢?

這種大量重複對象的問題尤其是在遊戲編程中經常存在,比如五子棋遊戲,棋子的初始化,一個遊戲大廳存在成千上百萬對局,如果每個局中的棋子都初始化一個對象,那內存使用是相當可怕的,這種需要把通用的對象屬性,不變的對象屬性抽離出來,做共享是有必要的。

據說這種優化有一個學名:享元模式,沒有必要記住名字,但需要記住原理和場景,必須要提一句:注意不變的對象纔可以哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章