结构体内存泄漏,vs诊断工具

发布时间:2024年01月18日

问题

struct S
{
	int nX;
	int nY;
	vector<int> nVec;
} 

在调试过程中,打开vs的诊断工具,发现大量std::_Container_proxy[] 的实例,定位到struct S代码。问题是我并没有 new 任何一个struct S。

如何解决

  1. 用户说软件运行一段时间卡死了
  2. 打开诊断工具一看,进程内存持续上涨,定位到内存泄漏
  3. 打开堆内存分析,截取快照
    在这里插入图片描述
  4. 查看泄漏内存的位置,定位到结构体 S
    参考:【内存】VS检测内存泄漏,定位泄漏代码位置方法

询问ai得到

如果你没有使用newdelete进行显式的堆内存分配和释放,而仍然在堆内存中发现大量的S实例,可能有其他原因导致了动态内存的分配。以下是一些可能的原因:

  1. 容器的动态内存管理: 如果S被包含在某个容器中,例如std::vector<S>,当容器需要扩展大小时,它会动态分配内存来存储更多的元素。确保你对容器的使用是正确的,避免不必要的动态内存分配。

  2. 全局或静态变量: 如果S是全局变量或静态变量,它们的生命周期可能会导致在程序运行期间一直存在于内存中。确保这些变量的生命周期与程序的需求一致。

  3. 递归或循环调用: 如果在函数调用中递归使用了S,或者在循环中创建了多个实例,这可能导致堆内存中存在大量实例。确保递归和循环的终止条件和逻辑是正确的。

  4. 全局操作符重载: 如果对newdelete 进行了全局的操作符重载,它可能会导致堆内存的不寻常行为。检查是否存在全局重载的操作符,并确保其正确性。

  5. 内存泄漏: 尽管你没有显式使用newdelete,但是在某些情况下可能发生了内存泄漏。确保所有动态分配的资源都得到了适当的释放。

在排查问题时,你可以使用内存分析工具,例如Valgrind或Visual Studio的内存调试工具,来跟踪内存分配和释放的情况,以找出内存泄漏或异常分配的原因。此外,仔细审查涉及S的代码,确保它的使用是符合预期的。

全检查了没有发现问题

然后我补充了一些代码,用于统计结构体的个数

#pragma once
#include<iostream>
#include<vector>
using namespace std;
struct S
{
	static int instanceCount; // 静态计数器
	int nX;
	int nY;
	vector<int> nVec;

	S() : nX(0), nY(0)
	{
		std::cout << "S created operator: instanceCount = " <<++ instanceCount << std::endl;
	}
	~S()
	{
		nX = 0;
		nY = 0;
		nVec.clear();
		nVec.resize(0);
		std::cout << "S Destruction operator: instanceCount = " << --instanceCount << std::endl;
	}

	// 自定义复制构造函数
	S(const S& other)
		: nX(other.nX),
		nY(other.nY)
	{
		// 复制 vector 中的数据
		nVec = other.nVec; // 如果 vector 不包含动态分配的内存,可以使用默认复制构造函数
		std::cout << "S copy constructor = " << ++instanceCount << std::endl;
	}

	// 自定义赋值运算符
	S& operator=(const S& other)
	{
		if (this != &other) // 避免自我赋值
		{
			nX = other.nX;
			nY = other.nY;
			// 复制 vector 中的数据
			nVec = other.nVec; // 如果 vector 不包含动态分配的内存,可以使用默认赋值运算符

			std::cout << "S copy assignment operator instanceCount = " << instanceCount <<std::endl;
		}
		return *this;
	}

};

int S::instanceCount = 0; // 初始化计数器

main

#include"S.h"

int main()
{
	S a;
	a.nX = 12;
	a.nY = 5;
	a.nVec = vector<int>(3, 10);

	S b;
	
	S c = a;
	return 0;
}

在这里插入图片描述

我实际的工程运行时,发现结构体最大也就10个,只是在不停地调用构造函数和析构函数,并且内存持续在泄漏

然后我发现了这样一段代码:

void GetS(S & ar)
{
	memset(&ar, 0, sizeof(ar)); //这么写会导致vector内存泄漏
	ar.nX = 7;
	ar.nY = 9;
	ar.nVec = vector<int>(3, 1);
}

还原现场如下:

int main()
{

	for (int i = 0; i < 100; i++)
	{
		S a;
		a.nX = 12;
		a.nY = 5;
		a.nVec = vector<int>(3, 10);
		GetS(a);
	}
	return 0;
}

在这里插入图片描述
断点打在for运行前和运行后

在这里插入图片描述
可以看出确实泄漏100次

在你提供的 void GetS(S & ar) 函数中,有一个问题可能导致内存泄漏。问题出在以下这一行代码:

memset(&ar, 0, sizeof(ar));

这行代码试图将 ar 对象的内存全部设置为零。然而,S 类型的对象中包含了 std::vector 类型的成员变量 nVec ,而对于 std::vector 类型,它包含了指向动态分配内存的指针。因此,使用 memset 直接将整个对象的内存设置为零可能导致 std::vector 内存管理出现问题。

对于包含动态分配内存的对象,最好避免使用 memset 这样的内存清零函数。相反,可以使用合适的初始化方式,或者直接调用对象的构造函数。

在这个场景下,由于 ar 对象是一个传入的引用参数,通常可以在调用函数时确保传入的对象是有效的,无需使用 memset 来初始化。如果确实需要对 ar 进行初始化,可以考虑使用合适的构造函数,例如:

ar = S();  // 使用默认构造函数进行初始化

或者在 S 类中添加一个初始化函数,并在需要时手动调用。

避免使用 memset 直接操作包含动态分配内存的对象,以免破坏对象的内部结构和内存管理。

文章来源:https://blog.csdn.net/fuyouzhiyi/article/details/135652636
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。