一次踩内存事件

本文通过具体案例,详细介绍了如何排查和定位内存错误问题,包括使用调试工具观察指针变化、逐步缩小问题范围的方法。

做各个模块集成,其中包含了文件管理、录像业务相关的库。

背景:在文件管理初始化时,应用层会指定数据根目录。

 

测试发现在执行特定操作时,目录失效,无法创建文件,日志大概这样  (NULL) /Movie/xxxx.MP4,好像是指定的根目录变空了。 

检查一番, 没有进行去初始化, 跟文件管理库负责同事远程沟通了下,他在库里维护了一个指针,指向 应用 层设置的 目录字符串(这个方法当然不好,应该复制进去,后已修改),

在确认了应用 层没有改变这个 根目录的内容后, 可以肯定 这个指针在某个时间被修改为NULL。

 

用arm-xxxx-nm命令 检查相关文件管理 .a 静态库(没有使用.so库)的符号表, 发现数据段有指针RootDir及所在地址,为检测出是谁修改了这个指针变量, 

编译好程序后 用arm-xxx-gdb启程序, 启动前watch RootDir,再执行触发异常的操作,然后可喜的看到了,的确有地方把这个指针修改成0了, 可悲的是watch停下来的时候,代码已经跑过去了,无法发现是哪处代码修改了它,跑了多次发现每次停下来的地方还不一样。

为什么不是修改这个变量的时候停下来,目前还不清楚,可能跟多线程有关。

 

问题要继续解决,这个时候只能暴力方法+打印了:

首先看这个RootDir变量在执行程序中的符号地址,用arm-xxx-nm  app 得到它的运行时地址,例如0x40008008,

然后分析触发变NULL的可能的代码路径, 在这些代码中插入多处打印,打印 *(unsigned int*)0x40008008 即RootDir存储的地址,看它在什么情况下变为0, 由非0变为0之间的代码 可能就是踩内存的地方。

(这里可能有个问题,插入打印语句后, RootDir的地址是可能变化的,所以编译后要用nm命令检测一遍RootDir的地址是否还是0x40008008,如果不是,则将打印语句中的0x40008008修改成新地址,再次编译,这之后应该就是一致的)

 

然后用类似二分法逐步缩小检测范围,直至定位到一个录像相关函数,在这个函数前RootDir有正常值,在这个函数执行后,RootDir记录的地址变0, 99%可以确定是这一函数引起的问题。 

由于此函数是其它组件提供的库函数,交给相关负责人检查, 最终原因是数组操作越界,踩了一大片内存。为什么会踩到RootDir,这个是全局变量,编译进最终的可执行程序后,从地址上看,它和相关数组是相邻关系,然后被祸害了。。。

 

 

 

 

AIS(Artificial Intelligence System,人工智能系统)内存问题通常有以下原因及相应的解决办法: ### 原因 - **内存泄漏**:程序中动态分配的内存没有被正确释放,随着程序的运行,未释放的内存越来越多,最终导致内存被耗尽。例如在使用Python的TensorFlow框架时,如果在循环中不断创建新的张量而不进行释放,就可能出现内存泄漏问题。 - **数据加载过大**:一次性加载大量的数据到内存中,超过了系统所能承受的范围。比如在进行图像识别任务时,将大量高清图像一次性读入内存进行处理。 - **模型参数过多**:复杂的深度学习模型往往具有大量的参数,在训练或推理过程中需要占用大量的内存。例如一些大型的语言模型,其参数量可能达到数十亿甚至上百亿。 - **多进程或多线程问题**:在多进程或多线程环境下,如果没有对内存使用进行合理的控制,多个进程或线程可能会同时占用大量内存,导致内存不足。 ### 解决办法 - **内存泄漏检测与修复**:使用专门的内存分析工具,如Python的`memory_profiler`库,来检测内存泄漏的位置。对于检测到的问题代码,确保在不再使用内存时及时释放。例如在Python中使用`del`关键字删除不再使用的对象,并调用`gc.collect()`进行垃圾回收: ```python import gc # 创建一个大对象 large_list = [i for i in range(1000000)] # 使用完后删除对象 del large_list # 手动触发垃圾回收 gc.collect() ``` - **数据分批次处理**:将大数据集分成小批次进行处理,避免一次性加载过多数据。例如在使用PyTorch进行数据加载时,可以使用`DataLoader`的`batch_size`参数来控制每次加载的数据量: ```python from torch.utils.data import DataLoader, Dataset class MyDataset(Dataset): def __init__(self): # 初始化数据集 pass def __getitem__(self, index): # 获取单个数据样本 pass def __len__(self): return 1000 # 创建数据集 dataset = MyDataset() # 创建数据加载器,设置批次大小 dataloader = DataLoader(dataset, batch_size=32) # 分批次处理数据 for batch in dataloader: # 处理批次数据 pass ``` - **模型优化**:对模型进行剪枝、量化等操作,减少模型的参数量。例如使用`torch.nn.utils.prune`模块对PyTorch模型进行剪枝: ```python import torch import torch.nn.utils.prune as prune # 定义一个简单的模型 model = torch.nn.Linear(10, 10) # 对模型的权重进行剪枝 prune.l1_unstructured(model, name='weight', amount=0.2) ``` - **进程与线程管理**:合理控制多进程或多线程的数量,避免同时运行过多的进程或线程。可以使用线程池或进程池来管理并发任务,例如在Python中使用`concurrent.futures`模块: ```python import concurrent.futures def task(): # 定义任务函数 pass # 创建线程池 with concurrent.futures.ThreadPoolExecutor(max_workers=4) as executor: # 提交任务 futures = [executor.submit(task) for _ in range(10)] # 等待所有任务完成 concurrent.futures.wait(futures) ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值