new 内存不足 求助

时间:2021-11-06 22:46:57
MFC写的一个基于消息的异步套节字通讯,出问题的代码是在接收文件的时候,发送方以while循环的方式发送,接收方在OnSocket消息中不停的接收,在接收第二个带文件内容的数据报发生了内存不足的情况,我通过写日志的方式跟踪到是New时不成功报出的错误,现在把代码贴出来,并附上运行日志,请大家帮忙分析原因。

void CCallDlg::OnSock(WPARAM wParam,LPARAM lParam)
{
DWORD dwMaxNum = 0;
int curPackSize=0;//当前数据报大小
CString sExtra="",mQSerial="",mQueues="",mFilePath="",sCurLog="";
switch(LOWORD(lParam))
{
case FD_READ:
{
sCurLog="1. 检查套节字寄存器可读取的字节数!\r\n";
OnWriteLog(sCurLog);

            ::ioctlsocket(mCallSocket,FIONREAD,&dwMaxNum);//

sCurLog.Format("2. 寄存器端口处发现%d个字节的可读数据,并开始申请堆内存空间!\r\n",dwMaxNum);
OnWriteLog(sCurLog);

            char* pBuffer =NULL;
pBuffer=new char[dwMaxNum]; //定义一个缓冲区
if(!pBuffer)
{
sCurLog="2-1. 堆内存数组pBuffer申请失败!\r\n";
OnWriteLog(sCurLog);
}
            char* m_pchBuffer=NULL;
            memset(pBuffer,0,dwMaxNum);

sCurLog="3. 数据成功考备到堆内存数组pBuffer中!\r\n";
OnWriteLog(sCurLog);

CPackage mPacMsg;
WSABUF wsabuf;

wsabuf.buf=pBuffer;
wsabuf.len=(unsigned long)dwMaxNum;

DWORD dwRead=0;
DWORD dwFlag=0;
SOCKADDR_IN addrFrom;//接收到来的地址信息
int len=sizeof(SOCKADDR);

sCurLog="4. 开始从套节字端口读取数据!\r\n";
OnWriteLog(sCurLog);

if(SOCKET_ERROR==WSARecvFrom(mCallSocket,&wsabuf,1,&dwRead,&dwFlag,
(SOCKADDR*)&addrFrom,&len,NULL,NULL))
{
//             DWORD error=WSAGetLastError();
//         WSASendError(error);
MessageBox("从服务器获取数据失败,请重启或联系相关部门解决!");
return;
}//先接收到数据,然后分类处理
//MessageBox("收到一个包!");
sCurLog.Format("5. 从套节字端口读取数据!共读取%d个字节!\r\n",dwRead);
OnWriteLog(sCurLog);

            memcpy(&mPacMsg,wsabuf.buf,sizeof(mPacMsg));//从pBuffer中起始位置考备sizeof(Msg)个字节到&Msg地址中
curPackSize=mPacMsg.m_dwDatalen;//记录该次数据报大小

sCurLog="6. 信息包获取完毕!\r\n";
OnWriteLog(sCurLog);

if(mPacMsg.m_nMsgType==MT_SENDDATA)
{
sCurLog="7. 开始获取完整数据报!\r\n";
OnWriteLog(sCurLog);
m_pchBuffer = new char[mPacMsg.m_dwDatalen];
memset(m_pchBuffer,0,mPacMsg.m_dwDatalen);
memcpy(m_pchBuffer,wsabuf.buf,dwMaxNum);//dwMaxNum与dwDatalen大小应该一致
sCurLog="8. 数据报获取完毕,并开始分析!\r\n";
OnWriteLog(sCurLog);
switch(mPacMsg.m_nSubType)
{
case ST_FILE://语音文件
{
if (m_bFirst|| mPacMsg.m_nFlag==-1)//一个文件的首次接收
{
m_bFirst=false;
mFilePath=".\\voice\\测试.txt";
// mFilePath.Format(".\\voice\\%s.wav",CPublicData::mDeptName);
m_File.Open(mFilePath,CFile::modeCreate|CFile::modeReadWrite);
char* pchTmp = m_pchBuffer + sizeof(CPackage);
m_File.WriteHuge(pchTmp,curPackSize-sizeof(CPackage));
pchTmp = NULL;

if (mPacMsg.m_dwFileLen==curPackSize-sizeof(CPackage))
{
m_File.Close();
m_bFirst = TRUE;
}
}
else if (mPacMsg.m_nFlag==0) //文件传输过程中
{
char* pTmp = m_pchBuffer + sizeof(CPackage);
m_File.WriteHuge(pTmp,curPackSize-sizeof(CPackage));
pTmp = NULL;
}
else if (mPacMsg.m_nFlag==1) //文件传输结束
{
char* pTmp = m_pchBuffer + sizeof(CPackage);
m_File.WriteHuge(pTmp,curPackSize-sizeof(CPackage));
m_bFirst = TRUE;
m_File.Close();
pTmp = NULL;
}
}
break;
case ST_IMAGE://图片文件
{
}
break;
}
sCurLog="9. 释放堆内存:pBuffer与m_pchBuffer!\r\n";
OnWriteLog(sCurLog);
delete []pBuffer;
pBuffer=NULL;

delete [] m_pchBuffer;
m_pchBuffer = NULL;

return;
}
else//MT_SENDDATA消息应该被首先检测,其它消息中不需要用到pBuffer,最先判断后,如果该数据报不是MT_SENDDATA消息时,即可以及时释放pBuffer堆内存
{
sCurLog="10. 释放堆内存:pBuffer!\r\n";
OnWriteLog(sCurLog);
delete []pBuffer;
pBuffer=NULL;
}//EndIF

.
省略部份代码
.
}
}
}


OnWriteLog(sCurLog);为写运行日志函数,现在把运行日志贴出来
1. 检查套节字寄存器可读取的字节数!
2. 寄存器端口处发现8192个字节的可读数据,并开始申请堆内存空间!
3. 数据成功考备到堆内存数组pBuffer中!
4. 开始从套节字端口读取数据!
5. 从套节字端口读取数据!共读取6280个字节!
6. 信息包获取完毕!
7. 开始获取完整数据报!
8. 数据报获取完毕,并开始分析!
9. 释放堆内存:pBuffer与m_pchBuffer!
1. 检查套节字寄存器可读取的字节数!
2. 寄存器端口处发现6280个字节的可读数据,并开始申请堆内存空间!

从日志可以看出,接收第二个数据报时,在日志2处报“内存不足”崩溃了。从程序中看就是在pBuffer=new char[dwMaxNum]; //定义一个缓冲区,时没有成功。
搞不明白,第一时执行时,我new 完有正常释放的啊,为什么第二次接收再申请时就内存不足了呢?

9 个解决方案

#1


任务管理器看看内存增长情况,  程序中间有多个return 可能容易造成内存泄漏,
  推荐把缓冲区指针放到类中,方便管理, 一次申请 多次使用

#2


引用 1 楼 zgl7903 的回复:
任务管理器看看内存增长情况,  程序中间有多个return 可能容易造成内存泄漏,
  推荐把缓冲区指针放到类中,方便管理, 一次申请 多次使用

奇怪的是从任务管理器上看,内存并没有增加什么,因为才接收第二个包就内存不足了。一次申请多次使用怕不行哦,因为我每次接收的数据报大小可能不一样。

#3


看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了

#4


引用 3 楼 oyljerry 的回复:
看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了




引用 3 楼 oyljerry 的回复:
看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了

从最后一条日志
2. 寄存器端口处发现6280个字节的可读数据,并开始申请堆内存空间!
看,申请的空间为6280个字节大小,也不算多啊。

#5


1. 现代的PC都有虚拟内存的技术,很少出现 内存不足 这种错误,即使偶尔出现,也是运行很长时间以后才会出现的。
2. 检查你的代码,看一下是不是new和delete配对了,不配对肯定有内存泄露。至少我看到了WSARecvFrom读取错误的部分代码没有delete。

#6


引用 5 楼 shenyi0106 的回复:
1. 现代的PC都有虚拟内存的技术,很少出现 内存不足 这种错误,即使偶尔出现,也是运行很长时间以后才会出现的。
2. 检查你的代码,看一下是不是new和delete配对了,不配对肯定有内存泄露。至少我看到了WSARecvFrom读取错误的部分代码没有delete。

确实,这里是个失误,不过程序问题应该不发生在该处,从日志看,WSARecvFrom一直没有进入错误处理部分。

#7


是否存在越界操作的问题?  数据包是否完整有判断吗?

memcpy(m_pchBuffer,wsabuf.buf,dwMaxNum); //很怀疑这一句  屏蔽掉看看

#8


把后续的对缓冲区的操作注释掉试试。
new 会抛出异常,不会返回 NULL 指针的。

#9


引用 7 楼 zgl7903 的回复:
是否存在越界操作的问题?  数据包是否完整有判断吗?

memcpy(m_pchBuffer,wsabuf.buf,dwMaxNum); //很怀疑这一句  屏蔽掉看看


从日志看,错误不发生在此处。就是在New处报错的。

#1


任务管理器看看内存增长情况,  程序中间有多个return 可能容易造成内存泄漏,
  推荐把缓冲区指针放到类中,方便管理, 一次申请 多次使用

#2


引用 1 楼 zgl7903 的回复:
任务管理器看看内存增长情况,  程序中间有多个return 可能容易造成内存泄漏,
  推荐把缓冲区指针放到类中,方便管理, 一次申请 多次使用

奇怪的是从任务管理器上看,内存并没有增加什么,因为才接收第二个包就内存不足了。一次申请多次使用怕不行哦,因为我每次接收的数据报大小可能不一样。

#3


看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了

#4


引用 3 楼 oyljerry 的回复:
看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了




引用 3 楼 oyljerry 的回复:
看看new的时候dwMaxNum申请的内存到底有多大,是不是超过了

从最后一条日志
2. 寄存器端口处发现6280个字节的可读数据,并开始申请堆内存空间!
看,申请的空间为6280个字节大小,也不算多啊。

#5


1. 现代的PC都有虚拟内存的技术,很少出现 内存不足 这种错误,即使偶尔出现,也是运行很长时间以后才会出现的。
2. 检查你的代码,看一下是不是new和delete配对了,不配对肯定有内存泄露。至少我看到了WSARecvFrom读取错误的部分代码没有delete。

#6


引用 5 楼 shenyi0106 的回复:
1. 现代的PC都有虚拟内存的技术,很少出现 内存不足 这种错误,即使偶尔出现,也是运行很长时间以后才会出现的。
2. 检查你的代码,看一下是不是new和delete配对了,不配对肯定有内存泄露。至少我看到了WSARecvFrom读取错误的部分代码没有delete。

确实,这里是个失误,不过程序问题应该不发生在该处,从日志看,WSARecvFrom一直没有进入错误处理部分。

#7


是否存在越界操作的问题?  数据包是否完整有判断吗?

memcpy(m_pchBuffer,wsabuf.buf,dwMaxNum); //很怀疑这一句  屏蔽掉看看

#8


把后续的对缓冲区的操作注释掉试试。
new 会抛出异常,不会返回 NULL 指针的。

#9


引用 7 楼 zgl7903 的回复:
是否存在越界操作的问题?  数据包是否完整有判断吗?

memcpy(m_pchBuffer,wsabuf.buf,dwMaxNum); //很怀疑这一句  屏蔽掉看看


从日志看,错误不发生在此处。就是在New处报错的。