2017-02-24 33 views
0

的列表中給出此代碼內存溢出的例外:爲波蘇斯

public class Customer 
{ 
    public int CustomerID { get; set; } 
    public string Name { get; set; } 
    public List<Qualification> Qualifications { get; set; } 
} 

public class Qualification 
{ 
    public QualificationType QualificationType { get; set; } 
    public decimal Value { get; set; } 
} 

public class Action 
{ 
    public ActionID { get; set; } 
    public int CustomerID { get; set; } 
    public decimal ActionValue { get; set; } 
} 

public class Service : IService 
{ 
    public List<Customer> ProcessCustomers() 
    { 
     List<Customer> customers = _customerService.GetCustomers(); // 250,000 Customers 
     List<Action> actions = _actionService.GetActions(); // 6,000 

     foreach (var action in actions) { 
      foreach (affectedCustomer in customers.Where(x => x.CustomerID < action.CustomerID)) { 
       affectedCustomer.Qualifications.Add(new Qualification { QualificationType = QualificationType.Normal, Value = action.ActionValue}); 
      } 

      foreach (affectedCustomer in customers.Where (x => SpecialRules(x))) { 
       affectedCustomer.Qualifications.Add(new Qualification { QualificationType = QualificationType.Special, Value = action.ActionValue}); 
      } 
     } 
    } 
} 

的「最有資格」的客戶可以擁有12000個資質結束。平均而言,客戶可能最終獲得約100個資格。

但是,在大約50個動作被處理之後,我很早就得到了一個OOME。在那個時候,我的List仍然只有250,000個客戶,但是在整個客戶中增加了約5,000,000個資格。

這很多嗎?似乎有點讓我印象深刻。我懷疑我可以有數千萬的客戶,每個客戶平均擁有1000個資格,並且仍然很好。我甚至沒有接近這一點。

我可以在代碼中做些什麼來提高效率?我意識到我可以將每個(或批量分組)的結果寫入數據庫,但我寧願在寫入結果之前儘可能在內存中做更多的事情。


這樣做是週期通過6000個動作,每個動作,增加了資格對於一些客戶的可變數目。對於每個操作,所有具有customerID> = Action-Causing客戶的客戶都將添加一個資格認證。這就是~12億增加的記錄。另外,對於每一項行動,8-10名客戶都會獲得資格認證。與12億美元相比,只有6萬條記錄。

我試圖在內存中這樣做,因爲我不想將數十億記錄插入到數據庫中。在下一步處理中,我將需要這種記錄分離方式,它將從上到下查看客戶資格和客戶ID步驟的差異。儘管最終我最終將結果(比SUM更復雜)放在數據庫中。但我只能通過查看個人資格差異的步驟來達到這些結果,如曲線上的評分。

+0

我很樂意提供幫助,如果你告訴我你的代碼應該做什麼:) –

+0

@EyalPerry我添加了目標。 – Suamere

回答

1

您下載的對象數量真的很大 - 您應該考慮以較小的塊處理數據,而不是一次下載全部數據。

在.NET中,單個對象的a limit of memory - 您從不被允許創建超過2 GiB的單個對象。它在用於陣列的.NET 4.5的64位上爲has been lifted

列表正在將數據存儲在數組中。如果您將所有數據下載到一個列表中,則底層陣列的大小超出限制,並且您有OutOfMemory異常。

+0

我不明白爲什麼'List '變大,如果'Something'變大。我認爲這個集合只是一個指向對象的指針集合。顯然,這不是在C#中。有沒有辦法做到這一點? – Suamere

0

我一直在鼓吹SOLID Code和顯式域模型很長一段時間的重要性。我沒有被迫寫域邏輯,你必須在幾年內考慮成千上萬的數據點。這是我已經找到關於.NET OOME:

  1. 對象的集合,不是指向對象的集合。收集本身就是其各部分的總和。
  2. 對於32位應用程序,應用程序可以使用〜2GiB。因此,即使您將大集合分成更小的集合集,也無法查看大量的數據集。
  3. 對象沒有靜態地址。 .Net可以自由移動物體,除非你讓你的代碼unsafe並強制物體粘滯。但即使你這樣做,單個對象仍然受到最大2GiB大小的限制(這很好),並且該應用程序仍然受到最大2GiB內存的限制。所以創建一個指針集合不是一個選項。
  4. Web應用程序(Web API和ASP.Net)不能使用IMAGE_FILE_LARGE_ADDRESS_AWARE標誌,或者從我所知道的很容易的64個大型應用程序運行,我希望聽到其他情況。

不幸的解決方案

我需要打破我的域模型,並做一些黑客。例如:而不是資格的名單,我可以自由地計算並相加,我必須有一個Customer類,像這樣:

public class Customer 
{ 
    public int CustomerID { get; set; } 
    public string Name { get; set; } 
    public decimal QualificationType1WithVariableType1Total { get; set; } 
    public decimal QualificationType1WithVariableType2Total { get; set; } 
    public decimal QualificationType2WithVariableType1Total { get; set; } 
    public decimal QualificationType2WithVariableType2Total { get; set; } 
} 

切實做好所有的計算了前面,如果我介紹其他變量,我將不得不有一個「總」變量來處理。這意味着;客戶沒有向客戶添加數千條記錄,而只有六個預先計算的字段,意思是我可以稍後用於計算。

所以我能夠減少我的記憶足跡,但我不再能夠明確地使用我的域,並自由地進行計算,同時觀察一大組結果。

當然,這些屬性在技術上已經存在了。有些是Readonly,根據計數,平均值和總和執行LINQ特殊方程。有些是基於100個客戶ID上下線性鏈中其他客戶的讀取/寫入。但相反,我必須拋棄所有的背景,並僅使用總計。

我只是很沮喪,在這個時代,我必須打破我的上下文領域模型才能在硬件的約束下工作。我的應用程序的速度非常快,已經在O(1)附近縮放,所以速度不是問題。