簡體   English   中英

POCO列表的內存不足異常

[英]OutOfMemory Exception for List of POCOs

給出以下代碼:

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});
            }
        }
    }
}

“最合格”客戶可能最終獲得12,000個合格條件。 平均而言,客戶可能會獲得約100個資格。

但是在處理了大約50個動作之后,我很早就得到了OOME。 到那時,我的列表中仍然只有250,000個客戶,但是在整個客戶中添加了大約5,000,000個資格。

那很多嗎 在我看來似乎有些不知所措。 我懷疑我可能有數以千萬計的客戶,並且每個客戶平均擁有1000個資格,並且仍然可以。 我什至沒有接近。

我該怎么做才能使代碼更有效? 我知道我可以將每個(或大批)動作的結果寫入數據庫,但是我寧願在寫入結果之前盡可能多地在內存中進行操作。


這是通過6,000個操作循環進行的,並且對於每個操作,都會增加一些可變數量客戶的資格。 對於每個操作,其customerID> =引起操作的客戶的所有客戶都將添加資格。 這樣就增加了約12億條記錄。 此外,對於每個操作,8-10個客戶都會獲得資格證書。 僅有6萬條記錄,而12億條記錄。

我試圖在內存中執行此操作,因為我不想在數據庫中進行數十億條記錄插入操作。 在下一步的處理中,我將需要此記錄分離,以查看客戶資格以及客戶ID從上到下的步驟差異。 盡管最后,我還是將結果(比SUM復雜)放入數據庫中。 但是我只能通過查看各個資格條件上的差異步驟(例如在曲線上進行評分)來獲得這些結果。

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

在.NET ,單個對象有一個內存限制 -絕對不允許創建超過2 GiB的單個對象。 對於陣列的.NET 4.5,它已提升為64位。

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

長期以來,我一直在宣傳SOLID代碼和顯式域模型的重要性。 我並沒有被迫編寫域邏輯,而在過去的幾年中,您不得不考慮成千上萬個數據點。 這是關於.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; }
}

有效地預先進行所有計算,如果我引入其他變量,則必須有一個“總計”變量才能使用。 這樣做意味着 該客戶沒有給客戶添加數千條記錄,而是僅擁有六個預先計算的字段,這意味着我以后可以在計算中使用。

因此,我能夠減少內存占用,但是我不再能夠顯式使用我的域並在觀察大量結果的同時自由地進行計算。

當然,這些屬性在技術上已經存在。 有些是只讀的,並根據計數,平均值和總和執行LINQ特殊方程式。 其中一些是根據線性鏈中上下100個CustomerID中其他客戶的進度進行讀/寫的。 但是,相反,我必須放棄所有上下文,而只使用總計。

在這個時代,我不得不打破我的上下文域模型,以在硬件的約束下工作,這讓我很沮喪。 我的應用程序的速度非常快,並且已經縮放到O(1)左右,因此速度不是問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM