CSharpFlink分布式實時計算,OutOfMemoryException異常,你意想不到的原因。


 目錄

一、測試過程及問題

 二、問題排查及分析過程

 三、問題分析及解決過程

 四、問題解決初步結果


 一、測試過程及問題

     從昨天15點左右開始測試,1個主節點,10個計算節點,1000個數據點,每個數據點3(1個實時窗口,2個延遲窗口)個數據窗口,每個數據點隨時生成窗口周期和計算實例,每個數據點隨時生成實時數據或歷史數據。

     測試結果,由於程序無法再獲得電腦的內存而停止工作,更專業的說是System. OutOfMemoryException。

     主節點,今天3點左右開始出現異常,如下:

[20-11-13 03:00:21]>>窗口0952-補發數據_CSharpFlink.Core.Window.Operator.Min-線程(0033):【2020/11/13 2:00:00-2020/11/13 3:00:00】,異常:
Exception of type 'System.OutOfMemoryException' was thrown.   at System.Text.StringBuilder.ToString()
   at CSharpFlink.Core.Task.MasterTaskManager.ParallelCalculate(ICalculateContext context) in \CSharpFlink\src\CSharpFlink.Core\Task\MasterTaskManager.cs:line 358

[20-11-13 03:00:35]>>窗口0927-補發數據_CSharpFlink.Core.Window.Operator.Min-線程(0098):【2020/11/13 2:00:00-2020/11/13 3:00:00】,異常:
Exception of type 'System.OutOfMemoryException' was thrown.   at System.Text.RegularExpressions.Match..ctor(Regex regex, Int32 capcount, String text, Int32 begpos, Int32 len, Int32 startpos)
   at System.Text.RegularExpressions.RegexRunner.InitMatch()
   at System.Text.RegularExpressions.RegexRunner.Scan(Regex regex, String text, Int32 textbeg, Int32 textend, Int32 textstart, Int32 prevlen, Boolean quick, TimeSpan timeout)
   at System.Text.RegularExpressions.Regex.Run(Boolean quick, Int32 prevlen, String input, Int32 beginning, Int32 length, Int32 startat)
   at System.Text.RegularExpressions.Match.NextMatch()
   at System.Text.RegularExpressions.RegexReplacement.Replace(Regex regex, String input, Int32 count, Int32 startat)
   at System.Text.RegularExpressions.Regex.Replace(String input, String replacement)
   at CSharpFlink.Core.Task.MasterTaskManager.ParallelCalculate(ICalculateContext context) in \CSharpFlink\src\CSharpFlink.Core\Task\MasterTaskManager.cs:line 358

[20-11-13 03:00:42]>>窗口0941-補發數據_CSharpFlink.Core.Window.Operator.Avg-線程(0085):【2020/11/13 2:00:00-2020/11/13 3:00:00】,異常:
Exception of type 'System.OutOfMemoryException' was thrown.   at System.GC.AllocateNewArray(IntPtr typeHandle, Int32 length, Boolean zeroingOptional)
   at System.GC.AllocateUninitializedArray[T](Int32 length)
   at System.Buffers.TlsOverPerCoreLockedStacksArrayPool`1.Rent(Int32 minimumLength)
   at System.Text.ValueStringBuilder.Grow(Int32 additionalCapacityBeyondPos)
   at System.Text.ValueStringBuilder.Append(ReadOnlySpan`1 value)
   at System.Text.RegularExpressions.RegexReplacement.Replace(Regex regex, String input, Int32 count, Int32 startat)
   at System.Text.RegularExpressions.Regex.Replace(String input, String replacement)
   at CSharpFlink.Core.Task.MasterTaskManager.ParallelCalculate(ICalculateContext context) in \CSharpFlink\src\CSharpFlink.Core\Task\MasterTaskManager.cs:line 358
ValueStringBuilder.Append(ReadOnlySpan`1 value)
   at System.Text.RegularExpressions.RegexReplacement.Replace(Regex regex, String input, Int32 count, Int32 startat)
   at System.Text.RegularExpressions.Regex.Replace(String input, String replacement)
   at CSharpFlink.Core.Task.MasterTaskManager.ParallelCalculate(ICalculateContext context) in \CSharpFlink\src\CSharpFlink.Core\Task\MasterTaskManager.cs:line 358

[20-11-13 03:00:46]>>窗口0970-補發數據_CSharpFlink.Core.Window.Operator.Sum-線程(0074):【2020/11/13 2:00:00-2020/11/13 3:00:00】,異常:
Exception of type 'System.OutOfMemoryException' was thrown.   at System.String.Concat(String str0, String str1)
   at CSharpFlink.Core.Common.FileUtil.WriteAppend(String filePath, String[] contents) in \CSharpFlink\src\CSharpFlink.Core\Common\FileUtil.cs:line 36
   at CSharpFlink.Core.Task.MasterTaskManager.ParallelCalculate(ICalculateContext context) in \CSharpFlink\src\CSharpFlink.Core\Task\MasterTaskManager.cs:line 370

 從節點,部分存活,部分異常退出,異常信息如下:

[20-11-13 02:00:38]>>任務解析異常:
Exception of type 'System.OutOfMemoryException' was thrown.   at System.String.Concat(String str0, String str1)
   at CSharpFlink.Core.Common.FileUtil.WriteAppend(String filePath, String[] contents) in \CSharpFlink\src\CSharpFlink.Core\Common\FileUtil.cs:line 36
   at CSharpFlink.Core.Task.SlaveTaskManager.AddTask(String taskMsg) in \CSharpFlink\src\CSharpFlink.Core\Task\SlaveTaskManager.cs:line 138

       358行的代碼:

 CalculateContext calcContext=(CalculateContext)context;

       370行的代碼:

_masterCacheList.TryAdd(downTrans.Key, compressMsg);

        138行的代碼:

_slaveCacheList.TryAdd(downTrans.Key, downTrans);

         masterCacheList和slaveCacheList變量是ConcurrentDictionary類。

二、問題排查及分析過程

     共性問題:記錄的每處OutOfMemoryException異常信息都會涉及到對【String】的操作。

     第一步,使用dotnet-dump工具對String進行操作

     參考鏈接:https://docs.microsoft.com/zh-cn/dotnet/core/diagnostics/debug-memory-leak

       System.String有1784359個對象,為什么這么多對象呢?因為要生成計算節點的任務,這個任務要臨時保存到文件目錄中,把計算任務的文件發送到計算節點后,再進行刪除和清空程序緩存。

      寫任務文件其中涉及到FileUtil.WriteAppend()方法,這個和上面異常的日志信息是對應的,WriteAppend的代碼,如下:

public static void WriteAppend(string filePath, string[] contents)
{
    using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
    {
        fs.Seek(fs.Length, SeekOrigin.Current);
    string content = String.Join(Environment.NewLine, contents) + Environment.NewLine;
    byte[] data = System.Text.Encoding.UTF8.GetBytes(content);
    fs.Write(data, 0, data.Length);
    fs.Close();
     }
}
注:這是很早寫的代碼。

         其中Join函數,可能涉及到了Concat函數,和異常信息也是對應的。

        那就奇怪了,難道using和Close沒有起來關閉和釋放資源的目的嗎?讓我們來看看FileStream的基類Stream的Dispose和 Close都做了什么?看源代碼,如下圖:

          從代碼上看唯一做了SuppressFinalize函數操作,那么SuppressFinalize是什么意思呢?參見鏈接:

https://docs.microsoft.com/zh-cn/dotnet/api/system.gc.suppressfinalize?view=netcore-3.1

      上面鏈接的大概意思是:請求公共語言運行時不要調用指定對象的終結器。也就是說繼承了IDisposable接口,就不再調用類的析構函數了,那析構函數做了什么呢?如下圖:

           我們分析至此,Dispose和Close相當於什么都沒有做。那只能依賴GC來清理資源了。那在高並發下操作FileStream,Dispose和Close不起作用的情況下,難道GC沒有及時回收資源?看來有可能是這個問題。

三、問題分析及解決過程

       但是怎么解決這個問題呢?記得FileStream類有一個Flush函數,具體操作函數代碼,如下圖:

         Flush函數主要調用了FlushOSBuffer函數,代碼如下圖:

         沒有找到FlushFileBuffers函數,調用的函數,如下圖:

         這是非托管的代碼,函數參考鏈接:https://docs.microsoft.com/zh-cn/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers。大致意思是立即把數據寫到磁盤文件中,但是沒有找到該函數的源代碼。

       不管源代碼的事了,修改一下WriteAppend函數,加上Flush測試一下,代碼如下:

using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite))
{
    fs.Seek(fs.Length, SeekOrigin.Current);
string content = String.Join(Environment.NewLine, contents) + Environment.NewLine;
byte[] data = System.Text.Encoding.UTF8.GetBytes(content);
fs.Write(data, 0, data.Length);
fs.Flush();  //新增加代碼。
fs.Close();
}

 四、問題解決初步結果

     上午10點部署,測試到下午15點,總共5個小時左右的時間。內存使用情況,主節點基本維持在:380 MB(1000數據點,每個數據點有3個數據窗口,如果1個窗口,應該在130 MB左右),子節點基本維持在:150 MB。有一段時間,內存會逐步增漲,但是某個時間點內存會釋放到基本情況,曲線呈現正弦波趨勢。內存使用情況,如下圖:


物聯網&大數據技術 QQ群:54256083

物聯網&大數據合作 QQ群:727664080

網站:http://www.ineuos.net

聯系QQ:504547114

合作微信:wxzz0151

官方博客:https://www.cnblogs.com/lsjwq

iNeuOS工業互聯網操作系統 公眾號

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM