gpt4 book ai didi

c# - 不可检测的内存泄漏

转载 作者:行者123 更新时间:2023-11-30 16:23:21 27 4
gpt4 key购买 nike

我有一个 Stock 类,它从一个文件(大约 100 MB)加载大量股票历史数据。我有一个 Pair 类,它接受两个 Stock 对象并计算两者之间的一些统计关系,然后将结果写入文件。

在我的主要方法中,我有一个循环遍历成对股票列表(大约 500 只)。它创建 2 个库存对象,然后从这两个对象中创建一对对象。此时,对计算已写入文件,我已完成对象。我需要释放内存以便继续进行下一次计算。

除了将 3 个对象设置为 null 之外,我还在循环末尾添加了以下两行:

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();

跨过这两行似乎只是释放了每次循环迭代分配的 200-300 MB 中的 50 MB(从任务管理器中查看)。

程序执行了大约 8 到 10 对,然后才出现系统内存不足异常。内存使用量稳步增加,直到它在大约 1.5 GB 时崩溃。 (这是一台运行 Win7 Ultimate 的 8 GB 机器)

我在垃圾回收方面经验不多。我做错了什么吗?

这是我的代码,因为你问了:(注意:程序有两种模式,1> 添加模式,将新对添加到系统中。2> 常规模式,根据 filesystemwatcher 实时更新对文件> 事件。股票数据由名为 QCollector 的外部应用程序更新。)

这是 MainForm 中以添加模式运行的段:

foreach (string line in PairList)
{
string[] tokens = line.Split(',');

stockA = new Stock(QCollectorPath, tokens[0].ToUpper());
stockB = new Stock(QCollectorPath, tokens[1].ToUpper());

double ratio = double.Parse(tokens[2]);
Pair p = new Pair(QCollectorPath, stockA, stockB, ratio);

// at this point the pair is written to file (constructor handles this)

// commenting out the following lines of code since they don't fix the problem
// stockA = null;
// stockB = null;
// p = null;

// refraining from forced collection since that's not the problem
// GC.Collect(GC.MaxGeneration);
// GC.WaitForPendingFinalizers();

// so far this is the only way i can fix the problem by setting the pair classes
// references to StockA and StockB to null
p.Kill();
}

我正在根据要求添加更多代码:StockPairTimeSeries 的子类,它们具有共同的功能

public abstract class TimeSeries {
protected List<string> data;

// following create class must be implemented by subclasses (stock, pair, etc...)
// as each class is created differently, although their data formatting is identical
protected void List<string> Create();

// . . .

public void LoadFromFile()
{
data = new List<string>();

List<StreamReader> srs = GetAllFiles();

foreach (StreamReader sr in srs)
{
List<string> temp = new List<string>();
temp = TurnFileIntoListString(sr);
data = new List<string>(temp.Concat(data));
sr.Close()
}
}

// uses directory naming scheme (according to data month/year) to find files of a symbol
protected List<StreamReader> GetAllFiles()...

public static List<string> TurnFileIntoListString(StreamReader sr)
{
List<string> list = new List<string>();
string line;
while ((line = sr.ReadLine()) != null)
list.Add(line);
return list;
}

// this is the only mean to access a TimeSeries object's data
// this is to prevent deadlocks by time consuming methods such as pair's Create

public string[] GetListCopy()
{
lock (data)
{
string[] listCopy = new string[data.count];
data.CopyTo(listCopy);
return listCopy();
}
}
}

public class Stock : TimeSeries
{
public Stock(string dataFilePath, string symbol, FileSystemWatcher fsw = null)
{
DataFilePath = dataFilePath;
Name = symbol.ToUpper();
LoadFromFile();
// to update stock data when external app updates the files
if (fsw != null) fsw.Changed += FileSystemEventHandler(fsw_Changed);
}

protected void List<string> Create()
{
// stock files created by external application
}


// . . .
}

public class Pair : TimeSeries {
public Pair(string dataFilePath, Stock stockA, Stock stockB, double ratio)
{
// assign parameters to local members
// ...

if (FileExists())
LoadFromFile();
else
Create();
}

protected override List<string> Create()
{
// since stock can get updated by fileSystemWatcher's event handler
// a copy is obtained from the stock object's data
string[] listA = StockA.GetListCopy();
string[] listB = StockB.GetListCopy();
List<string> listP = new List<string>();

int i, j;
i = GetFirstValidBar(listA);
j = GetFirstValidBar(listB);
DateTime dtA, dtB;

dtA = GetDateTime(listA[i]);
dtB = GetDateTime(listB[j]);

// this hidden segment adjusts i and j until they are starting at same datetime
// since stocks can have different amount of data

while (i < listA.Count() && j < listB.Count)
{
double priceA = GetPrice(listA[i]);
double priceB = GetPrice(listB[j]);
double priceP = priceA * ratio - priceB;
listP.Add(String.Format("{0},{1:0.00},{2:0.00},{3:0.00}"
, dtA
, priceP
, priceA
, priceB
);
if (i < j)
i++;
else if (j < i)
j++;
else
{
i++;
j++;
}
}
}

public void Kill()
{
data = null;
stockA = null;
stockB = null;
}
}

最佳答案

你的内存泄漏在这里:

if (fsw != null) fsw.Changed += FileSystemEventHandler(fsw_Changed);

只要 FileSystemWatcher 处于事件状态,库存对象的实例就会保留在内存中,因为它正在响应 FileSystemWatcher 的事件。

我认为您想在其他地方实现该事件,或者在您的代码中的其他地方添加:

if (fsw != null) fsw.Changed -= fsw_Changed;

鉴于代码的编写方式,在完成批量处理的情况下,可能会在没有 FileSystemWatcher 的情况下调用 stock 对象。

在您发布的原始代码中,Stock 类的构造函数是使用 FileSystemWatcher 调用的。你现在已经改变了。我想您会发现现在使用空 FileSystemWatcher 可以删除您的 kill 并且不会有泄漏,因为您不再收听 fsw.Changed。

关于c# - 不可检测的内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11623130/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com