堆中的 DLL 内存泄漏
Posted
技术标签:
【中文标题】堆中的 DLL 内存泄漏【英文标题】:DLL Memory leaks in the heap 【发布时间】:2014-09-09 15:02:22 【问题描述】:我正在调试某人的代码并收到错误消息:这可能是由于堆损坏,这表明 .exe 或它已加载的任何 DLL 中存在错误。我研究并了解到这是由于一些内存泄漏,对象没有从堆中正确删除。我查看并查看了代码(逐行调试),它在返回语句处崩溃。这是代码:问题可能出在哪里?
首先是调用DLL的c#代码
public class Analysis
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1), Serializable]
public struct Pothole
public int Id;
public int FrameNumber;
public int LeftX;
public int TopY;
public int Width;
public int Height;
public int Certainty; // note that this is a percentage value, hence integer
public double Latitude;
public double Longitude;
public double Speed;
public double Area;
public double Volume;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 20)]
public string TimeCreated;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 200)]
public string CroppedImage;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 200)]
public string LeftImage;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 200)]
public string RightImage;
;
private int progress;
public int Progress
get return progress;
set progress = value;
public List<FileInfo> AllVideoFiles;
public List<FileInfo> FramesInDir;
public List<GpsPoint> GpsList;
public VideoMetaData MetaData;
public List<TrackPothole> PotholeList;
public VideoHandling VideoHandler;
public int verbose; //if verbose, then you get a lot of wordy output on what the program is doing
private VspDatabaseEntities1 _vde;
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void ProgressCallback(int value);
public event ProgressCallback OnProgressUpdated;
//initializes video files and calibraiton file names
[DllImport(@"Analyser.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int Initialize(string SettingsDir, int SettingDirLength, string LeftVideoFile, int LeftFileLength,
string RightVideoFile, int RightFileLength, string GpsCoordsFile, int GpsFileLength, string OutputDirectory, int OutputDirLength);
//analyse road files (do everything)
[DllImport(@"Analyser.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int Start([MarshalAs(UnmanagedType.FunctionPtr)] ProgressCallback callbackPointer, int verbose);
//Does the analysis of videos, uses the c++ analysis Start() function
public void Go(ProgressBar bar)
//flag -> should actually be set outside of function, can't find the outside
verbose = 1;
//obtain parameters from settings.settings
string inputDir = Properties.Settings.Default["InputDir"].ToString();
string outputDir = Properties.Settings.Default["OutputDir"].ToString();
string settingsDir = Properties.Settings.Default["SettingsDir"].ToString();
//setup directories and filenames
//string camfile = settingsDir + "\\" + "Calib.yaml"; => now we send the directory, not the specific file
string rightvid = "";
string leftvid = "";
string gpsfile = "";
//Associate multiple files according to creation times and loop through matching pairs
//(this works because of the copy operation? -> but won't everything have the same time then?)
AllVideoFiles = GetFiles(inputDir, "*.avi");
List<FileInfo> rightvids = new List<FileInfo>(AllVideoFiles.Where(v => v.FullName.Contains("3D_R")).OrderBy(v => v.CreationTime));
List<FileInfo> leftvids = new List<FileInfo>(AllVideoFiles.Where(v => v.FullName.Contains("3D_L")).OrderBy(v => v.CreationTime));
List<FileInfo> gpsfiles = new List<FileInfo>(GetFiles(inputDir, "*.txt").OrderBy(g => g.CreationTime));
Console.WriteLine("Got this far1");
//Check that the number of right and left video files and gps files match
int lowestCount = 0;
if (rightvids.Count != leftvids.Count)
MessageBox.Show("Error: The number of right camera videos does not match the number of left camera videos!");
if (rightvids.Count < leftvids.Count)
lowestCount = rightvids.Count;
else
lowestCount = leftvids.Count;
else
lowestCount = rightvids.Count;
if (gpsfiles.Count != lowestCount)
MessageBox.Show("Error: The number of gps data files does not match the number of right camera video files!");
if (gpsfiles.Count < lowestCount)
lowestCount = gpsfiles.Count;
//@todo
//Currently, looping through the files and hoping there is only one in the file.
Console.WriteLine("Got this far2");
for (int i = 0; i < lowestCount; i++)
rightvid = rightvids[i].FullName;
leftvid = leftvids[i].FullName;
gpsfile = gpsfiles[i].FullName;
if (verbose==1)
Console.WriteLine("C# FileNames: " + settingsDir + " " + outputDir + " " + leftvid + " " + gpsfile);
//Pass the filenames to the c++ functions
int InitReturn = Initialize(settingsDir, settingsDir.Length, leftvid, leftvid.Length,
rightvid, rightvid.Length, gpsfile, gpsfile.Length, outputDir, outputDir.Length);
if (verbose == 1)
Console.WriteLine("Initilize return value : " + InitReturn);
Report();
//Setup the progress bar
ProgressCallback callback = (value) => bar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
new Action(delegate() bar.Value = value; ));
//***RUN THE ANALYSIS
Console.WriteLine("Got this far4");
int start_ret_val = Start(callback, verbose);
if (verbose==1) Console.WriteLine("Start return value: " + start_ret_val);
//Create the pothole array
Pothole[] potArr = new Pothole[10000];
Thread.Sleep(1000); //@todo Why sleep?
if (start_ret_val > 0)
//fill up the array
///@todo How does this impact the memory wastage?
potArr = GetPotholes(start_ret_val);
MessageBox.Show("Analysis completed");
//bar.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal,
// new Action( delegate() bar.Value = 0; ));
else
MessageBox.Show("Analysis returned with error code : " + start_ret_val); //@todo Add some interpretation here
Console.WriteLine("Got this far5");
C++ 函数从这里开始:
cv::Mat RunClassifier(cv::Mat& frame, cv::Mat& mask, std::vector<CvANN_MLP*>& anns, std::vector<int>& criteria,std::vector<float>& thresholds, Mat& final_certainty)
//instantiate certainty matrix
std::vector<Mat> indiv_certs;
for (unsigned int i = 0; i < anns.size(); i++)
indiv_certs.push_back(Mat::zeros(frame.size(), CV_32FC1) );
Mat certainty = Mat::zeros(frame.size(), CV_32FC1);
final_certainty = Mat::zeros(frame.size(), CV_32FC1);
Mat final_image = frame.clone();
Mat* individual_masks = new Mat[anns.size()]();
for (unsigned int iter = 0; iter < anns.size();iter++)
individual_masks[iter] = Mat::zeros(frame.rows,frame.cols,CV_32F);
Mat final_mask = Mat::zeros(frame.rows,frame.cols,CV_32F);
//Convert the image to HSV colorspace (You could probably save a bit more if you converted and normalised the frame to 32F beforehand
// and not individually for each channel.
Mat hsv;
std::vector<Mat> hsv_channels;
cvtColor(frame, hsv, CV_BGR2HSV);
split(hsv, hsv_channels);
Mat hue, value, saturation;
hsv_channels.at(HUE).convertTo(hue, CV_32F);
hue /= HUE_MAX;
hue = 1- hue;
hsv_channels.at(SATURATION).convertTo(saturation, CV_32F);
Mat sat_pure = saturation.clone();
saturation /= SATURATION_MAX;
hsv_channels.at(VALUE).convertTo(value, CV_32F);
value /= VALUE_MAX;
//loop through the anns
//Declare multithreading variables
PCERT_DATA pCert_Array[3];
DWORD dwThreadIdArray[3];
HANDLE hThreadArray[3];
for (unsigned int ann_iter = 0; ann_iter < anns.size(); ann_iter++)
//allocate memory for thread data
pCert_Array[ann_iter] = (PCERT_DATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CERT_DATA) );
bool fft_flag = FALSE;
//Convert the image block according to the ann
switch (criteria.at(ann_iter))
case HUE:
pCert_Array[ann_iter]->converted_frame = &hue;
break;
case SATURATION:
pCert_Array[ann_iter]->converted_frame = &saturation;
break;
case VALUE:
pCert_Array[ann_iter]->converted_frame = &value;;
break;
case FREQUENCY:
pCert_Array[ann_iter]->converted_frame = &sat_pure;
fft_flag = TRUE;
break;
default:
return Mat();
//assign data to memory
pCert_Array[ann_iter]->ann = anns.at(ann_iter);
pCert_Array[ann_iter]->certainty = &(indiv_certs[ann_iter]);
pCert_Array[ann_iter]->mask = &mask;
pCert_Array[ann_iter]->fft_flag = fft_flag;
pCert_Array[ann_iter]->individual_mask = &(individual_masks[ann_iter]);
pCert_Array[ann_iter]->threshold = thresholds.at(ann_iter);
//call CalculateCertainty
hThreadArray[ann_iter] = CreateThread(
NULL, // default security attributes
0, // use default stack size
ComputeCertainty, // thread function name
pCert_Array[ann_iter], // argument to thread function
0, // use default creation flags
&dwThreadIdArray[ann_iter]); // returns the thread identifier
//wait for all the threads to stop computing
WaitForMultipleObjects(anns.size(), hThreadArray, TRUE, INFINITE);
//summing operation of certanties
for (unsigned int i = 0; i < anns.size(); i++)
certainty += indiv_certs[i];
//AND all the various ANN outputs
Mat temp = Mat::zeros(individual_masks[0].size(), individual_masks[0].type() );
for (unsigned int i = 0; i < anns.size(); i++)
temp += individual_masks[i];
Mat temp_binary_mask = (temp == anns.size());
temp = Mat::ones(individual_masks[0].size(), individual_masks[0].type() );
temp.copyTo(final_mask, temp_binary_mask);
cv::multiply(final_mask, certainty, final_certainty, 1.0/anns.size() );
delete individual_masks;
//deallocate threading memory used
for(unsigned int i=0; i<anns.size(); i++)
CloseHandle(hThreadArray[i]);
if(pCert_Array[i] != NULL)
HeapFree(GetProcessHeap(), 0, pCert_Array[i]);
pCert_Array[i] = NULL; // Ensure address is not reused.
return final_mask;
【问题讨论】:
如果您混合使用 Debug 和 Release DLL 或 EXE,您会得到这个“错误”(在这种情况下,这不是错误,而是由不同的内存分配器引起的行为差异)。跨度> @crashmstr 我将编辑帖子并添加 c#。我检查了,我没有混合 DLL 或 EXE 文件 请在 c++ 代码中为 individual_masks 使用向量。避免指向 cv::Mat 【参考方案1】:您是否尝试过使用内存泄漏检测工具,例如Visual Leak Detector?
在下面的代码中,
Mat* individual_masks = new Mat[anns.size()]();
应该使用operator delete[]
删除,所以delete[] individual_masks;
此外,无论内存泄漏如何,都应该进行数组边界检查。
for (unsigned int ann_iter = 0; ann_iter < anns.size(); ann_iter++)
//allocate memory for thread data
pCert_Array[ann_iter] = (PCERT_DATA) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(CERT_DATA) );
anns
是输入参数,所以它的大小不会是固定值,而是在PCERT_DATA pCert_Array[3];
中声明了pCert_Array
。
【讨论】:
感谢您的指点。我们在三个特征上训练神经网络,所以在我们的例子中,anns 总是三个,但它不是很好的编程实践。所以即使你说我应该删除 Mat 指针对象,我仍然遇到同样的问题,我试图让 VLD 仍然工作以上是关于堆中的 DLL 内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章