From 44ce8cc61c38b2728e1af7b56b671345f92f5fd0 Mon Sep 17 00:00:00 2001 From: liusiyang Date: Wed, 3 Jun 2026 09:48:45 +0800 Subject: [PATCH] offline tmp --- .../include/CheckErrorCodeDefine.hpp | 2 +- AlgorithmModule/include/ImageAllResult.h | 2 + AlgorithmModule/src/ImageAllResult.cpp | 43 +++++++ AlgorithmModule/src/ImgCheckAnalysisy.cpp | 2 + example/MemMonitor.h | 50 ++++++++ example/deal.cpp | 110 +++++++++++++++++- example/deal.h | 1 + example/test_example.cpp | 5 - 8 files changed, 208 insertions(+), 7 deletions(-) create mode 100644 example/MemMonitor.h diff --git a/AlgorithmModule/include/CheckErrorCodeDefine.hpp b/AlgorithmModule/include/CheckErrorCodeDefine.hpp index 63cfbec..3b72020 100644 --- a/AlgorithmModule/include/CheckErrorCodeDefine.hpp +++ b/AlgorithmModule/include/CheckErrorCodeDefine.hpp @@ -26,7 +26,7 @@ // resize 图片的 宽度 #define RESIZE_IMAGE_WIDTH 1680 -// #define AI_Time +#define AI_Time enum TEM_IMG_IDX_ { diff --git a/AlgorithmModule/include/ImageAllResult.h b/AlgorithmModule/include/ImageAllResult.h index d9976de..20f17b3 100644 --- a/AlgorithmModule/include/ImageAllResult.h +++ b/AlgorithmModule/include/ImageAllResult.h @@ -153,6 +153,8 @@ public: void AddLog(std::string str); // 添加图片信息 int AddDetImage(std::shared_ptr p); + // ✅ 处理完成后释放中间数据,减少内存占用 + void ReleaseIntermediateData(); DetStep getStep(); void setStep(DetStep step); bool IsNotDet(); diff --git a/AlgorithmModule/src/ImageAllResult.cpp b/AlgorithmModule/src/ImageAllResult.cpp index 8640da7..2389ae4 100644 --- a/AlgorithmModule/src/ImageAllResult.cpp +++ b/AlgorithmModule/src/ImageAllResult.cpp @@ -18,6 +18,49 @@ ImageAllResult::~ImageAllResult() { } +// ✅ 处理完成后释放中间 cv::Mat 数据,减少内存占用 +// 注意:CheckResult(result)中的图像数据必须保留! +void ImageAllResult::ReleaseIntermediateData() +{ + std::lock_guard lock_cam(mtx_Det); + + // ✅ 释放原始输入图像(处理完成后不再需要) + if (result && result->in_shareImage) + { + if (!result->in_shareImage->img.empty()) + { + result->in_shareImage->img.release(); + } + if (!result->in_shareImage->AI_maskImg.empty()) + { + result->in_shareImage->AI_maskImg.release(); + } + } + + // ✅ AI_Time 开启时需要保留 detImg(用于生成缺陷小图)和 AI_Qx_MaskList(用于 AI 调试图像) + // 其他中间数据可以安全释放 +#ifndef AI_Time + if (!detImg.empty()) { detImg.release(); } +#endif + if (!AI_detImg.empty()) { AI_detImg.release(); } + if (!AIMaskImg.empty()) { AIMaskImg.release(); } + if (!AI_127CellMaskImg.empty()) { AI_127CellMaskImg.release(); } + if (!resultImg.empty()) { resultImg.release(); } + if (!shieldImg.empty()) { shieldImg.release(); } + if (!AI_shieldImg.empty()) { AI_shieldImg.release(); } + if (!YX_MaskImg.empty()) { YX_MaskImg.release(); } + if (!LcakPol_MaskImg.empty()) { LcakPol_MaskImg.release(); } + + // 释放 AI 检测中间结果 + if (qx_DetAIResult) { qx_DetAIResult->Init(); } + if (cell127_DetAIResult) { cell127_DetAIResult->Init(); } + + // ✅ AI_Time 开启时需要保留 AI_Qx_MaskList(GetAIDetImg 使用) +#ifndef AI_Time + AI_Qx_MaskList.clear(); +#endif +} + void ImageAllResult::AddLog(std::string str) { LogList.push_back(str); diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 9d05963..8de1640 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -666,6 +666,8 @@ int ImgCheckAnalysisy::Run(int nId) CheckRun(); m_nRun_Status = CHECK_THREAD_STATUS_COMPLETE; m_pImageAllResult->setStep(ImageAllResult::DetStep_Complet); + // ✅ 处理完成后立即释放 ImageAllResult 的中间数据,减少内存占用 + m_pImageAllResult->ReleaseIntermediateData(); m_nRun_Status = CHECK_THREAD_STATUS_IDLE; // printf("*--------%d\n", m_nRun_Status); } diff --git a/example/MemMonitor.h b/example/MemMonitor.h new file mode 100644 index 0000000..2128928 --- /dev/null +++ b/example/MemMonitor.h @@ -0,0 +1,50 @@ +#ifndef _MEM_MONITOR_H_ +#define _MEM_MONITOR_H_ + +#include +#include +#include +#include +#include + +// 获取当前进程 RSS (物理内存) 使用量,单位 MB +static inline long getRSS_KB() +{ + long rss = 0; + FILE *fp = fopen("/proc/self/status", "r"); + if (!fp) return -1; + + char line[256]; + while (fgets(line, sizeof(line), fp)) + { + if (strncmp(line, "VmRSS:", 6) == 0) + { + // 格式: "VmRSS: 12345 kB" + const char *p = line + 6; + while (*p == ' ' || *p == '\t') p++; + rss = atol(p); + break; + } + } + fclose(fp); + return rss; +} + +// 获取当前进程内存使用,打印格式化日志 +static inline void printMemUsage(const char *tag, const char *extra) +{ + long rss = getRSS_KB(); + struct rusage usage; + getrusage(RUSAGE_SELF, &usage); + printf("[MEM] %-30s | VmRSS: %6ld MB | maxRSS: %6ld MB | %s\n", + tag, rss / 1024, usage.ru_maxrss / 1024, extra ? extra : ""); +} + +// 获取产品检测 pipeline 队列深度信息快照 +#define MEM_LOG(tag, fmt, ...) \ + do { \ + long _rss = getRSS_KB(); \ + printf("[MEM] %-30s | VmRSS: %6ld MB | " fmt "\n", tag, _rss / 1024, ##__VA_ARGS__); \ + } while(0) + +#endif // _MEM_MONITOR_H_ diff --git a/example/deal.cpp b/example/deal.cpp index f8265bd..35e229b 100644 --- a/example/deal.cpp +++ b/example/deal.cpp @@ -3,6 +3,7 @@ #include #include #include +#include "MemMonitor.h" #include #include #include "CheckUtil.hpp" @@ -900,6 +901,7 @@ void deal::GetDealResultToQueu() mutex_Result_list.lock(); m_Result_list.push(checkResult); mutex_Result_list.unlock(); + m_nTotalResultsDrained.fetch_add(1); // ✅ 更新计数器 } else { @@ -1341,11 +1343,65 @@ int deal::CheckFileImg() products[i].print("产品" + std::to_string(i + 1)); } // getchar(); + printMemUsage("CheckFileImg-开始处理", ""); for (size_t i = 0; i < products.size(); i++) { - + char buf[128]; + snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理前", i+1, products.size(), products[i].product_id.c_str()); + printMemUsage(buf, ""); + CheckProduct(products[i]); // 处理单个产品 + + snprintf(buf, sizeof(buf), "产品[%zu/%zu] %s 处理后", i+1, products.size(), products[i].product_id.c_str()); + printMemUsage(buf, ""); + } + // ✅ 修复:所有产品处理完成后,通知后台线程退出 + printf("\n>>> 所有产品处理完成,正在停止后台线程...\n"); + + // 1. 通知产品读图线程退出 + m_bProductReadExit = true; + m_productTaskCV.notify_all(); + m_checkNotifyCV.notify_all(); + + // 2. 通知结果处理线程退出 + m_bExit = true; + cv.notify_all(); + + // 3. 等待产品读图线程结束 + for (auto &thread : m_productReadThreads) + { + if (thread && thread->joinable()) + { + thread->join(); + } + } + m_productReadThreads.clear(); + + // 4. 等待结果处理线程结束 + for (auto &thread : ptr_ResultthreadList) + { + if (thread && thread->joinable()) + { + thread->join(); + } + } + ptr_ResultthreadList.clear(); + + // 5. 等待 GetResult 线程结束 + if (ptr_GetResultthread && ptr_GetResultthread->joinable()) + { + ptr_GetResultthread->join(); } + ptr_GetResultthread.reset(); + + // 6. 等待 DealImg 线程结束(如果存在) + if (ptr_DealImgthread && ptr_DealImgthread->joinable()) + { + ptr_DealImgthread->join(); + } + ptr_DealImgthread.reset(); + + printf(">>> 所有后台线程已停止,程序退出.\n"); printf("\n"); // TODO: 后续实现具体功能 @@ -1386,6 +1442,11 @@ int deal::CheckProduct(const LoadProductImages &product) m_ProductFailedList.clear(); m_nCheckNotifiedCount = 0; + // ✅ 修复内存泄漏:清理上一个产品累积的原始图像数据(cv::Mat) + // 注意:m_DetResult 是统计结果(轻量字符串/数字),不清除,跨产品累积 + m_ImageInfoList.clear(); + m_productIdList.clear(); + // 3. 打印产品信息 printf("\n"); printf("╔══════════════════════════════════════════════════════════\n"); @@ -1437,6 +1498,53 @@ int deal::CheckProduct(const LoadProductImages &product) SendCheckImg(info, IN_IMG_Status_End, true); printf("\n"); + // ✅ 流控:等待 Run 线程处理完当前产品 + 结果全部保存完毕,再继续下一个产品 + // 两步确认: + // 第1步 - 等 GetResultThread 把本产品所有结果从 m_CheckResultList 取出 + // 第2步 - 等 ResultThread 把 m_Result_list 中的结果全部保存、释放内存 + { + int startDrained = m_nTotalResultsDrained.load(); + int expectedResults = (int)totalImgNum; + int maxWaitMs = 600000; // 最多等待10分钟 + int waitedMs = 0; + + // 第1步:等结果被消费 + printf("[流控-1] 等待产品 %s 的 %d 个检测结果被消费...\n", product.product_id.c_str(), expectedResults); + while (m_nTotalResultsDrained.load() - startDrained < expectedResults && waitedMs < maxWaitMs) + { + usleep(100000); + waitedMs += 100; + } + printf("[流控-1] 已完成,已消费 %d 个结果\n", m_nTotalResultsDrained.load() - startDrained); + + // 第2步:等结果全部保存完毕(m_Result_list 排空) + printf("[流控-2] 等待结果保存完毕、内存释放...\n"); + waitedMs = 0; + while (waitedMs < maxWaitMs) + { + bool queueEmpty; + { + std::lock_guard lk(mutex_Result_list); + queueEmpty = m_Result_list.empty(); + } + if (queueEmpty) + { + // 再等一小会确保最后一批 I/O 完成 + usleep(500000); + break; + } + usleep(100000); + waitedMs += 100; + if (waitedMs % 10000 == 0) + { + size_t sz; + { std::lock_guard lk(mutex_Result_list); sz = m_Result_list.size(); } + printf("[流控-2] 等待中... 结果队列剩余 %zu 个\n", sz); + } + } + printf("[流控] 产品 %s 完全处理完毕,内存已释放,继续下一个产品\n\n", product.product_id.c_str()); + } + // 6. 打印统计(不清理线程,继续下一个产品) PrintProductStatistics(product); // getchar(); diff --git a/example/deal.h b/example/deal.h index 11f22b7..7ac3ed9 100644 --- a/example/deal.h +++ b/example/deal.h @@ -799,6 +799,7 @@ public: std::mutex m_checkNotifyMutex; // 通知队列锁 std::condition_variable m_checkNotifyCV; // 通知条件变量 std::atomic m_nCheckNotifiedCount{0}; // 已通知数量 + std::atomic m_nTotalResultsDrained{0}; // ✅ 已被消费的检测结果总数 std::set m_productIdList; // 产品ID列表 }; diff --git a/example/test_example.cpp b/example/test_example.cpp index 74e6828..ef3ed99 100644 --- a/example/test_example.cpp +++ b/example/test_example.cpp @@ -245,10 +245,5 @@ int main(int argc, char *argv[]) signal(SIGINT, handler); test.start(); - while (true) - { - usleep(10 * 1000); - } - return 0; }