設計背景
每個平台都會有用戶這種基礎數據的設計,作為最基礎的用戶,每個用戶都有很多屬性,比如性別,姓名,手機號等,每個用戶還可以有類似經驗值這樣的榮譽系統,根據不同的經驗值來對應不同的等級,不同的等級對應不同的榮譽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%,真實業務中,可以進行這種優化的地方數不勝數,不知道你是否在乎呢?
這種大量重復對象的問題尤其是在游戲編程中經常存在,比如五子棋游戲,棋子的初始化,一個游戲大廳存在成千上百萬對局,如果每個局中的棋子都初始化一個對象,那內存使用是相當可怕的,這種需要把通用的對象屬性,不變的對象屬性抽離出來,做共享是有必要的。
據說這種優化有一個學名:享元模式,沒有必要記住名字,但需要記住原理和場景,必須要提一句:注意不變的對象才可以哦