目錄
一、測驗程序及問題
二、問題排查及分析程序
三、問題分析及解決程序
四、問題解決初步結果
一、測驗程序及問題
從昨天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 =https://www.cnblogs.com/lsjwq/p/ 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 =https://www.cnblogs.com/lsjwq/p/ 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工業互聯網作業系統 公眾號

轉載請註明出處,本文鏈接:https://www.uj5u.com/net/214013.html
標籤:.NET Core
