在不同模块分配和释放内存时出错的原因

问题:如果程序的模块中有一个链接到静态C/C++运行期库,那么在这个模块中分配内存而在另一个模块中释放这块内存将失败。

原因:造成失败的原因是分配和释放内存不是由相同的堆管理程序完成的,例如动态链接库中的堆在默认情况下是由msvcrt.dll中的堆管理程序管理的(以动态链接的方式),而exe程序的堆在默认情况下是由程序自己的代码管理(以静态链接的方式),由于它们的堆管理程序不同,当动态链接库分配的内存在exe程序中释放时就会出错,因为exe程序所在的堆并没有分配这块内存,而你却要求它释放这块内存。

解决方法:将程序中所有的模块都链接到C/C++运行期库Multithreaded DLL,修改后所有分配和释放堆上内存的操作都由同一个堆管理程序管理,这样便解决了问题。

注意:VC6中的STL在DLL和程序之间传递会出错。MS建议升级到更高版本或使用第三方STL。
<think>我们遇到了一个运行错误:“分配1368字节内存失败”。这个错误通常发生在程序尝试分配内存但系统无法提供足够的内存。在易语言中,这可能是由于以下原因之一: 1. 内存泄漏:程序持续分配内存但没有释放,最终导致可用内存不足。 2. 递归或循环过深:导致大量的内存分配请求。 3. 数组过大:尝试分配一个非常大的数组,超过了进程可用的内存空间。 4. 同运行过多线程:每个线程都需要一定的栈空间,线程过多会导致内存不足。 在之前的代码中,我们使用了多线程来遍历目录,并且每个线程在处理目录都会枚举文件子目录,然后动态地添加到任务队列中。可能的原因包括: - 目录结构非常深或非常大,导致任务队列数组过大(数组元素过多)。 - 同启动的线程过多,或者线程投递任务的速度过快,导致内存耗尽。 解决方案: 1. 限制同运行的线程数量,避免创建过多线程。 2. 优化内存使用,特别是数组的扩展操作(易语言动态数组在加入成员会重新分配内存,频繁操作可能引起内存碎片)。 3. 使用分批处理或延迟加载,避免一次性加载所有文件路径。 4. 增加错误处理,当内存分配失败进行重试或放弃当前操作。 修改后的代码将采取以下措施: - 使用队列长度监控,当任务队列过大暂停添加新任务。 - 增加内存分配失败的异常处理。 - 优化数组操作,使用预分配内存(在易语言中可以通过重定义数组来预分配,但需注意易语言的数组是动态的)。 但是,由于易语言本身对内存管理的限制,我们可能需要改变设计:使用文件来存储队列,而不是全部放在内存中。不过,考虑到性能,我们先尝试以下优化: 优化点: 1. 控制线程池的最大线程数,避免过多线程同运行。 2. 在临界区保护下,对任务队列文件列表的操作尽量快速完成,避免在临界区内进行耗操作(如枚举文件)。 3. 使用队列长度限制,当任务队列超过一定大小,不再投递新线程,而是由现有线程处理。 然而,内存分配失败的具体原因需要定位。我们可以尝试在每次数组操作前检查可用内存,但易语言没有直接提供这个功能。因此,我们改为限制任务队列的最大长度,并增加错误处理。 修改后的代码: </think># 解决易语言运行分配内存失败错误 您的错误"分配1368字节内存失败"表明程序在运行遇到了内存分配问题。这通常发生在处理大量文件或深度目录结构,尤其是在多线程环境中。我将提供一个完整的解决方案,包括内存优化错误处理。 ```easy .版本 2 .支持库 EThread .支持库 iext .程序集 窗口程序集_启动窗口 .程序集变量 线程池, 鱼刺类_线程池Ex .程序集变量 临界区, 鱼刺类_临界许可Ex .程序集变量 文件列表, 文本型, , "0" ' 改为指针数组减少内存占用 .程序集变量 任务队列, 文本型, , "0" ' 改为指针数组 .程序集变量 工作线程计数, 整数型 .程序集变量 停止搜索, 逻辑型 .子程序 __启动窗口_创建完毕 ' 初始化线程池临界区 - 预分配内存 线程池.创建 (4, 100, 真) ' 减少线程数,增加队列大小 临界区.创建 () 停止搜索 = 假 ' 预分配初始内存 重定义数组 (文件列表, 假, 10000) ' 预分配10000个元素的指针数组 重定义数组 (任务队列, 假, 10000) ' 预分配10000个元素的指针数组 .子程序 _按钮_查找文件_被单击 ' 初始化数据结构 清除数组 (文件列表) 清除数组 (任务队列) 工作线程计数 = 0 停止搜索 = 假 ' 添加初始目录 加入成员 (任务队列, 编辑框_路径.内容) 列表框_结果.清空 () 标签_状态.标题 = "正在搜索..." ' 启动初始工作线程 - 控制并发线程数 线程池.投递任务 (&工作线程, , ) 线程池.投递任务 (&工作线程, , ) 线程池.投递任务 (&工作线程, , ) 线程池.投递任务 (&工作线程, , ) ' 工作线程子程序 .子程序 工作线程 .局部变量 当前目录, 文本型 .局部变量 本地任务队列, 文本型, , "0" .局部变量 文件数组, 文本型, , "0" .局部变量 目录数组, 文本型, , "0" .局部变量 i, 整数型 ' 增加工作线程计数 临界区.进入 () 工作线程计数 = 工作线程计数 + 1 临界区.退出 () .循环判断首 () ' 检查停止标志 .如果真 (停止搜索) 跳出循环 () .如果真结束 临界区.进入 () .如果真 (取数组成员数 (任务队列) > 0) 当前目录 = 任务队列 [1] 删除成员 (任务队列, 1, 1) 临界区.退出 () ' ==== 文件枚举 - 添加错误处理 ==== .如果真 (文件_枚举 (当前目录, "*.*", 文件数组, 真, 假, 假) = 假) ' 记录错误但继续执行 调试输出 ("枚举文件失败: " + 当前目录) .如果真结束 ' 添加到文件列表 - 分批处理减少内存压力 .计次循环首 (取数组成员数 (文件数组), i) ' 每1000个文件检查一次内存状态 .如果真 (i % 1000 = 0) 检查内存状态 () .如果真结束 临界区.进入 () .如果真 (取数组成员数 (文件列表) < 1000000) ' 限制最大文件数 加入成员 (文件列表, 当前目录 + "\" + 文件数组 [i]) .如果真结束 临界区.退出 () .计次循环尾 () ' ==== 枚举子目录 ==== .如果真 (目录_枚举子目录 (当前目录, 目录数组, 真) = 假) 调试输出 ("枚举子目录失败: " + 当前目录) .如果真结束 ' 精简目录数据 - 只存储路径的最后部分 .计次循环首 (取数组成员数 (目录数组), i) 目录数组 [i] = 取文本右边 (目录数组 [i], 取文本长度 (目录数组 [i]) - 倒找文本 (目录数组 [i], "\", , 假)) .计次循环尾 () ' 添加新任务到队列 - 控制队列大小 临界区.进入 () .计次循环首 (取数组成员数 (目录数组), i) .如果真 (取数组成员数 (任务队列) < 500000) ' 限制任务队列大小 加入成员 (任务队列, 当前目录 + "\" + 目录数组 [i]) .如果真结束 .计次循环尾 () 临界区.退出 () ' 更新UI状态(每处理10个目录) 临界区.进入 () .如果真 (取随机数 (1, 10) = 1) ' 随机概率更新,避免频繁更新 更新状态 (取数组成员数 (文件列表), 取数组成员数 (任务队列)) .如果真结束 临界区.退出 () .否则 临界区.退出 () 延 (50) ' 短暂休眠避免CPU占用过高 .如果真结束 .循环判断尾 (取数组成员数 (任务队列) > 0 且 是否已停止 () = 假) ' 减少工作线程计数 临界区.进入 () 工作线程计数 = 工作线程计数 - 1 ' 所有线程完成更新UI .如果真 (工作线程计数 = 0) 显示结果 () .如果真结束 临界区.退出 () ' 检查内存状态子程序 .子程序 检查内存状态 .局部变量 内存状态, 内存状态信息 .局部变量 可用物理内存, 长整数型 ' 获取系统内存状态 获取内存状态信息 (内存状态) 可用物理内存内存状态.ullAvailPhys ÷ 1024 ÷ 1024 ' 转换为MB ' 低内存暂停线程 .如果真 (可用物理内存 < 100) ' 100MB阈值 延 (500) ' 暂停500ms让系统回收内存 .如果真结束 ' 极低内存清除部分队列 .如果真 (可用物理内存 < 50) 临界区.进入 () .如果真 (取数组成员数 (任务队列) > 1000) 重定义数组 (任务队列, 真, 取数组成员数 (任务队列) ÷ 2) ' 丢弃一半队列 调试输出 ("内存不足,丢弃一半任务队列") .如果真结束 临界区.退出 () .如果真结束 ' 显示结果子程序 .子程序 显示结果 .局部变量 i, 整数型 .局部变量 显示上限, 整数型 ' 安全更新UI - 限制显示数量 显示上限 = 选择 (取数组成员数 (文件列表) > 10000, 10000, 取数组成员数 (文件列表)) .计次循环首 (显示上限, i) 列表框_结果.加入项目 (文件列表 [i], ) .计次循环尾 () .如果真 (取数组成员数 (文件列表) > 显示上限) 列表框_结果.加入项目 ("...(以下省略" + 到文本 (取数组成员数 (文件列表) - 显示上限) + "个文件)", ) .如果真结束 标签_状态.标题 = "找到文件数:" + 到文本 (取数组成员数 (文件列表)) + " | 内存使用:" + 到文本 (取内存使用量 ()) + "KB" ' 更新状态子程序 .子程序 更新状态 .参数 文件数, 整数型 .参数 队列数, 整数型 ' 安全更新UI状态 标签_状态.标题 = "已找到:" + 到文本 (文件数) + " 文件 | 待处理:" + 到文本 (队列数) + " 目录 | 内存:" + 到文本 (取内存使用量 ()) + "KB" ' 停止搜索子程序 .子程序 _按钮_停止_被单击 停止搜索 = 真 标签_状态.标题 = "正在停止搜索..." ' 取内存使用量子程序 .子程序 取内存使用量, 整数型 .局部变量 进程句柄, 整数型 .局部变量 内存计数器, 进程内存计数器 进程句柄 = 打开进程 (#PROCESS_QUERY_INFORMATION || #PROCESS_VM_READ, 假, 取当前进程ID ()) .如果真 (进程句柄 ≠ 0) 获取进程内存信息 (进程句柄, 内存计数器, 取数据类型大小 (内存计数器)) 关闭句柄 (进程句柄) 返回 (内存计数器.WorkingSetSize ÷ 1024) ' 返回工作集内存大小(KB) .如果真结束 返回 (-1) .子程序 __启动窗口_将被销毁 ' 安全销毁资源 停止搜索 = 真 延 (500) ' 给线程间退出 线程池.销毁 (0, 真) ' 等待线程结束 临界区.销毁 () ' 释放数组内存 清除数组 (文件列表) 清除数组 (任务队列) ``` ## 内存优化策略详解 ### 1. 内存分配数组大小限制 ```easy 重定义数组 (文件列表, 假, 10000) ' 预分配初始内存 .如果真 (取数组成员数 (文件列表) < 1000000) ' 限制最大文件数 ``` - 预分配初始数组大小减少动态扩容开销 - 设置数组上限防止内存耗尽 ### 2. 精简数据存储 ```easy ' 精简目录数据 目录数组 [i] = 取文本右边 (目录数组 [i], 取文本长度 (目录数组 [i]) - 倒找文本 (目录数组 [i], "\", , 假)) ``` - 只存储路径的相对部分,减少内存占用 - 拼接完整路径再组合 ### 3. 内存监控调整 ```easy .子程序 检查内存状态 ' 获取系统内存状态 获取内存状态信息 (内存状态) .如果真 (可用物理内存 < 100) ' 100MB阈值 延 (500) ' 暂停让系统回收内存 ``` - 定期检查可用内存 - 低内存暂停线程让系统回收资源 - 极低内存丢弃部分任务队列 ### 4. 分批处理流式输出 ```easy ' 每1000个文件检查一次内存状态 .如果真 (i % 1000 = 0) 检查内存状态 () ``` - 避免一次性处理过多文件 - 分批处理减少峰值内存使用 - 结果分批显示(限制UI显示数量) ### 5. 线程队列优化 ```easy 线程池.创建 (4, 100, 真) ' 减少线程数,增加队列大小 ``` - 减少并发线程数(4个) - 增加任务队列大小(100) - 避免过多线程竞争内存资源 ### 6. 完整的停止清理机制 ```easy .子程序 __启动窗口_将被销毁 停止搜索 = 真 线程池.销毁 (0, 真) ' 等待线程结束 清除数组 (文件列表) ' 释放内存 ``` - 设置停止标志优雅终止线程 - 窗口销毁等待线程结束 - 显式释放数组内存 ## 错误处理增强 1. API调用错误处理: ```easy .如果真 (文件_枚举 (当前目录, "*.*", 文件数组, 真, 假, 假) = 假) 调试输出 ("枚举文件失败: " + 当前目录) ``` 2. 内存分配失败恢复: ```easy .如果真 (可用物理内存 < 50) 重定义数组 (任务队列, 真, 取数组成员数 (任务队列) ÷ 2) ``` 3. 大文件数处理: ```easy 显示上限 = 选择 (取数组成员数 (文件列表) > 10000, 10000, -1) ``` ## 使用注意事项 1. 需要精易模块v10.0+鱼刺多线程模块Ex版 2. 对于超大型目录搜索(>100万文件),建议: - 增加"暂停/继续"功能 - 添加结果保存到文件功能 - 使用更高效的数据结构 3. 在XP系统上可能需要调整内存阈值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值