C# 无法检测的内存泄漏
我有一个C# 无法检测的内存泄漏,c#,garbage-collection,C#,Garbage Collection,我有一个Stock类,它从一个文件(大约100MB)加载大量的股票数据历史记录。我有一个Pair类,它接受两个Stock对象,计算它们之间的一些统计关系,然后将结果写入文件 在我的主要方法中,我有一个循环遍历一组股票(大约500只)。它创建两个库存对象,然后从两个库存对象中创建一个配对对象。在这一点上,成对的计算被写入到文件中,我已经完成了对对象的处理。我需要释放内存以便继续下一次计算 除了将3个对象设置为null之外,我还在循环末尾添加了以下两行: GC.Collect(GC.MaxGener
Stock
类,它从一个文件(大约100MB)加载大量的股票数据历史记录。我有一个Pair
类,它接受两个Stock
对象,计算它们之间的一些统计关系,然后将结果写入文件
在我的主要方法中,我有一个循环遍历一组股票(大约500只)。它创建两个库存对象,然后从两个库存对象中创建一个配对对象。在这一点上,成对的计算被写入到文件中,我已经完成了对对象的处理。我需要释放内存以便继续下一次计算
除了将3个对象设置为null之外,我还在循环末尾添加了以下两行:
GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
跨过这两行似乎只是从每个循环迭代分配的200-300MB中释放出50MB(从任务管理器查看)
该程序在给我一个系统内存不足异常之前,会执行大约八到十对操作。内存使用率稳步增加,直到崩溃到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();
}
我正在根据请求添加更多代码:Stock
和Pair
是TimeSeries
的子类,它们具有相同的功能
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;
}
}
公共抽象类时间序列{
受保护名单数据;
//以下创建类必须由子类(stock、pair等)实现
//虽然每个类的数据格式相同,但它们的创建方式不同
受保护的无效列表创建();
// . . .
公共void LoadFromFile()
{
数据=新列表();
List srs=GetAllFiles();
foreach(srs中的StreamReader sr)
{
列表温度=新列表();
temp=TurnFileIntoListString(sr);
数据=新列表(临时混凝土(数据));
高级关闭()
}
}
//使用目录命名方案(根据数据月/年)查找符号的文件
受保护的列表GetAllFiles()。。。
公共静态列表TurnFileIntoListString(StreamReader sr)
{
列表=新列表();
弦线;
而((line=sr.ReadLine())!=null)
列表。添加(行);
退货清单;
}
//这是访问TimeSeries对象数据的唯一方法
//这是为了通过耗时的方法(如pair的Create)防止死锁
公共字符串[]GetListCopy()
{
锁(数据)
{
string[]listCopy=新字符串[data.count];
data.CopyTo(listCopy);
返回listCopy();
}
}
}
公共类股票:TimeSeries
{
公开股票(字符串dataFilePath,字符串符号,FileSystemWatcher fsw=null)
{
DataFilePath=DataFilePath;
Name=symbol.ToUpper();
LoadFromFile();
//当外部应用程序更新文件时更新股票数据
如果(fsw!=null)fsw.Changed+=FileSystemEventHandler(fsw\u Changed);
}
受保护的无效列表创建()
{
//由外部应用程序创建的库存文件
}
// . . .
}
公共类对:TimeSeries{
公共对(字符串数据文件路径、股票A、股票B、双倍比率)
{
//将参数指定给本地成员
// ...
如果(FileExists())
LoadFromFile();
其他的
创建();
}
受保护的覆盖列表创建()
{
//因为股票可以通过fileSystemWatcher的事件处理程序进行更新
//从股票对象的数据中获取副本
字符串[]listA=StockA.GetListCopy();
字符串[]listB=StockB.GetListCopy();
List listP=新列表();
int i,j;
i=GetFirstValidBar(列表A);
j=GetFirstValidBar(列表B);
日期时间dtA,dtB;
dtA=GetDateTime(列表A[i]);
dtB=GetDateTime(listB[j]);
//此隐藏段调整i和j,直到它们同时开始
//因为股票可以有不同数量的数据
而(i
您的内存泄漏在这里:
if (fsw != null) fsw.Changed += FileSystemEventHandler(fsw_Changed);
只要FileSystemWatcher处于活动状态,stock对象的实例就会保存在内存中,因为它正在响应FileSystemWatcher的事件
我认为您希望在其他地方实现该事件,或者在代码的其他地方添加一个:
if (fsw != null) fsw.Changed -= fsw_Changed;
考虑到代码的编写方式,在批量处理完成的情况下,可能需要在没有FileSystemWatcher的情况下调用stock对象
在您发布的原始代码中,Stock类的构造函数被FileSystemWatcher调用