
感觉好像是哪个变量申请了内存没有释放,然后越用越多,大概 20-30 来个请求增加 0.1G 的内存,我没有 delete 一些指针的话,增加的更快。
程序主要是通过 socket 通信,用 c++进行目标检测的这段代码。
op 只学过 c++的基本语法,平时做深度学习 python 用的比较多,所以 c++这边可能会欠缺一点,只能说把一些 bug 修复了能跑起来的水平。其实也是拿着官方 demo 改的,参数直接构造了一个 fake command ,见笑了
#include "jetson-utils/videoSource.h" #include "jetson-utils/videoOutput.h" #include "jetson-utils/imageLoader.h" #include "jetson-inference/detectNet.h" #include "jetson-inference/objectTracker.h" #include "cJSON.h" #include "jetson-utils/Socket.h" #include &l;signal.h> #include <vector> using namespace std; struct PredObj { float x1; float y1; float x2; float y2; float score; int classID; float width; float height; const char *classDesc; }; bool signal_recieved = false; void sig_handler(int signo) { if (signo == SIGINT) { LogVerbose("received SIGINT\n"); signal_recieved = true; } } int usage() { printf("usage: detectnet [--help] [--network=NETWORK] [--threshold=THRESHOLD] ...\n"); printf(" input [output]\n\n"); printf("Locate objects in a video/image stream using an object detection DNN.\n"); printf("See below for additional arguments that may not be shown above.\n\n"); printf("positional arguments:\n"); printf(" input resource URI of input stream (see videoSource below)\n"); printf(" output resource URI of output stream (see videoOutput below)\n\n"); printf("%s", detectNet::Usage()); printf("%s", objectTracker::Usage()); printf("%s", videoSource::Usage()); printf("%s", videoOutput::Usage()); printf("%s", Log::Usage()); return 0; } int main(int argc, char **argv) { /* * parse command line */ commandLine cmdLine(argc, argv); if (cmdLine.GetFlag("help")) return usage(); /* * create detection network */ detectNet *net = detectNet::Create(cmdLine); if (!net) { LogError("detectnet: failed to load detectNet model\n"); return 1; } // parse overlay flags const uint32_t overlayFlags = detectNet::OverlayFlagsFromStr(cmdLine.GetString("overlay", "box,labels,conf")); Socket *socket = Socket::Create(SOCKET_TCP); if (!socket) { printf("failed to create socket\n"); return 0; } // bind the socket to a local port if (!socket->Bind("0.0.0.0", 8899)) { printf("failed to bind socket\n"); return 0; } if (!socket->Accept(0)) { printf("failed to accept socket\n"); return 0; } printf("server is running\n"); if (cmdLine.GetFlag("help")) return usage(); /* * attach signal handler */ if (signal(SIGINT, sig_handler) == SIG_ERR) LogError("can't catch SIGINT\n"); /* * create input stream */ while (true) { // receive a message uint8_t buffer[1024]; uint32_t remoteIP; uint16_t remotePort; size_t bytes = socket->Recieve(buffer, sizeof(buffer), &remoteIP, &remotePort); if (bytes == 0) { printf("close client\n"); return 0; } else { const char *receiveData = NULL; printf("received message: %s\n", buffer); cJSON *cjson_receive = cJSON_Parse((char *)buffer); receiveData = cJSON_Print(cjson_receive); printf("%s\n", receiveData); SAFE_DELETE(receiveData); // cJSON *fStickLat = cJSON_GetObjectItem(cjson_receive, "fStickLat"); // double m_fStickLat = cJSON_GetNumberValue(fStickLat); // printf("%.10f\n", m_fStickLat); // printf("m_fStickLat: %.10f\n", m_fStickLat); cJSON *fpath = cJSON_GetObjectItem(cjson_receive, "fpath"); char *m_fpath = cJSON_GetStringValue(fpath); printf("m_fpath: %s\n", m_fpath); // 检测到 socket 等于 shutdown ,关闭 socket if (strcmp((char *)buffer, "shutdown") == 0) { printf("shutting down\n"); break; } char av1[] = "detectnet"; char av2[] = "--network=ssd-mobilenet-v2"; char av3[] = "./socket_example/test1.jpg"; int len = strlen(m_fpath); char av3_2[len]; strcpy(av3_2, m_fpath); // char *fake_argv[3] = {av1, av2, m_fpath}; char *fake_argv[3] = {av1, av2, av3_2}; commandLine cmdLine(3, fake_argv); printf("t1\n"); cJSON_Delete(cjson_receive); // cJSON_Delete(fpath); // SAFE_DELETE(m_fpath); printf("t2\n"); /* * create input stream */ videoSource *input = videoSource::Create(cmdLine, ARG_POSITION(0)); if (!input) { LogError("detectnet: failed to create input stream\n"); // return 1; continue; } /* * create output stream */ videoOutput *output = videoOutput::Create(cmdLine, ARG_POSITION(1)); if (!output) { LogError("detectnet: failed to create output stream\n"); // return 1; continue; } /* * processing loop */ // while( !signal_recieved ) //{ // capture next image uchar3 *image = NULL; int status = 0; input->Capture(&image, &status); // if (!input->Capture(&image, &status)) // { // // if( status == videoSource::TIMEOUT ) // // continue; // // break; // EOS // } // detect objects in the frame detectNet::Detection *detectiOns= NULL; const int numDetectiOns= net->Detect(image, input->GetWidth(), input->GetHeight(), &detections, overlayFlags); vector<PredObj> predObjList; if (numDetections > 0) { LogVerbose("%i objects detected\n", numDetections); for (int n = 0; n < numDetections; n++) { LogVerbose("\ndetected obj %i class #%u (%s) cOnfidence=%f\n", n, detections[n].ClassID, net->GetClassDesc(detections[n].ClassID), detections[n].Confidence); LogVerbose("bounding box %i (%.2f, %.2f) (%.2f, %.2f) w=%.2f h=%.2f\n", n, detections[n].Left, detections[n].Top, detections[n].Right, detections[n].Bottom, detections[n].Width(), detections[n].Height()); if (detections[n].TrackID >= 0) // is this a tracked object? LogVerbose("tracking ID %i status=%i frames=%i lost=%i\n", detections[n].TrackID, detections[n].TrackStatus, detections[n].TrackFrames, detections[n].TrackLost); PredObj predObj; predObj.x1 = detections[n].Left; predObj.y1 = detections[n].Top; predObj.x2 = detections[n].Right; predObj.y2 = detections[n].Bottom; predObj.score = detections[n].Confidence; predObj.classID = detections[n].ClassID; predObj.width = detections[n].Width(); predObj.height = detections[n].Height(); predObj.classDesc = net->GetClassDesc(detections[n].ClassID); predObjList.push_back(predObj); } } // predObjList to json cJSON *cjson_send = cJSON_CreateObject(); cJSON *cjson_array = cJSON_CreateArray(); for (int i = 0; i < predObjList.size(); i++) { cJSON *cjson_obj = cJSON_CreateObject(); cJSON_AddNumberToObject(cjson_obj, "x1", predObjList[i].x1); cJSON_AddNumberToObject(cjson_obj, "y1", predObjList[i].y1); cJSON_AddNumberToObject(cjson_obj, "x2", predObjList[i].x2); cJSON_AddNumberToObject(cjson_obj, "y2", predObjList[i].y2); cJSON_AddNumberToObject(cjson_obj, "score", predObjList[i].score); cJSON_AddNumberToObject(cjson_obj, "classID", predObjList[i].classID); cJSON_AddNumberToObject(cjson_obj, "width", predObjList[i].width); cJSON_AddNumberToObject(cjson_obj, "height", predObjList[i].height); cJSON_AddStringToObject(cjson_obj, "classDesc", predObjList[i].classDesc); cJSON_AddItemToArray(cjson_array, cjson_obj); } cJSON_AddItemToObject(cjson_send, "predObjList", cjson_array); char *json_str = cJSON_Print(cjson_send); if (!socket->Send((void *)json_str, strlen(json_str), remoteIP, remotePort)) { // printf("failed to send message\n"); printf("client is disconnected\n"); break; } printf("t3\n"); //cJSON_Delete(cjson_send); cJSON_Delete(cjson_array); SAFE_DELETE(json_str); // render outputs if (false && output != NULL) { output->Render(image, input->GetWidth(), input->GetHeight()); // update the status bar char str[256]; sprintf(str, "TensorRT %i.%i.%i | %s | Network %.0f FPS", NV_TENSORRT_MAJOR, NV_TENSORRT_MINOR, NV_TENSORRT_PATCH, precisionTypeToStr(net->GetPrecision()), net->GetNetworkFPS()); output->SetStatus(str); // check if the user quit // if( !output->IsStreaming() ) // break; } // print out timing info net->PrintProfilerTimes(); printf("t4\n"); SAFE_DELETE(input); SAFE_DELETE(output); // SAFE_DELETE(image); //} } } /* * destroy resources */ LogVerbose("detectnet: shutting down...\n"); SAFE_DELETE(net); LogVerbose("detectnet: shutdown complete.\n"); return 0; } 1 momo1999 2024 年 5 月 16 日 你都知道你写的是 C++了,多用 RAII 自动释放啊,手动释放太难了。 |
2 YsHaNg 2024 年 5 月 16 日 TL;DR 扔给 GPT 了 Yes, there are several memory leaks in the provided code. Let's go through them: 1. In the loop where you receive messages via the socket, you allocate memory for `receiveData` using `cJSON_Print`, but you don't free it. You should free it after you're done using it. 2. Similarly, you create a `cJSON` object using `cJSON_Parse`, but you don't free it using `cJSON_Delete`. 3. You allocate memory for `json_str` using `cJSON_Print`, but you don't free it after sending it over the socket. 4. You allocate memory for `input` and `output` inside the loop, but you don't free them before the next iteration of the loop. This can lead to memory leaks if `input` or `output` fail to initialize. To fix these memory leaks: - Free `receiveData` after its use with `SAFE_DELETE(receiveData)`. - Delete the `cjson_receive` object using `cJSON_Delete` after its use. - Free `json_str` after sending it over the socket. - Delete `input` and `output` objects at the end of each iteration of the loop before reinitializing them. Here's how you can do it: ```cpp SAFE_DELETE(receiveData); cJSON_Delete(cjson_receive); SAFE_DELETE(json_str); SAFE_DELETE(input); SAFE_DELETE(output); ``` Place these lines at appropriate locations in your code to ensure memory allocated dynamically is properly deallocated. |
3 albert0yyyy OP |
4 albert0yyyy OP @shuax 我去看看 RAII ,感谢 |
5 YsHaNg 2024 年 5 月 16 日 @albert0yyyy RAII+smart ptr 多用 auto 会让生活好很多 |
6 albert0yyyy OP @YsHaNg #5 感谢,我去看看。 |
7 watzds 2024 年 5 月 16 日 内存越用越多也正常啊,看能增加到多少 |
8 hello2090 2024 年 5 月 16 日 你实在调不出来的话,code 一部分一部分的 comment 掉试呗 |
9 bfjm 2024 年 5 月 16 日 1. 可以使用 valgrind 看看哪里会有内存泄漏 2. 看这个 cJSON 的情况是分配了内存,需要手动调用他的内存分配函数,你可以使用 RAII 封装一下 类似这种 ```c++ class smart_ptr { smart_ptr(char *buffer) { } ~smart_ptr() { } }; ``` |
10 bfjm 2024 年 5 月 16 日 class smart_ptr { smart_ptr(char *buffer) { cjson = cJSON_Parse((char *)buffer) } ~smart_ptr() { cJSON_Delete(cjson_receive); } cJSON *cjson }; @bfjm |
11 bfjm 2024 年 5 月 16 日 另外你先得了解清除,这里面谁分配了内存,(谁分配谁释放),分配的是堆内存还是栈内存,栈内存不用手动释放,堆内存需要手动释放 |
12 bfjm 2024 年 5 月 16 日 我不太确定 cJSON *cjson_obj = cJSON_CreateObject(); 这里分配内存没有,这里会比较可疑 |
13 sidyhe 2024 年 5 月 16 日 查代码比较难的话, 用 tcmalloc 检测泄露, 它会告诉你哪里的内存没有释放 |
14 cnbatch 2024 年 5 月 17 日 单凭肉眼观察发现以下几个内存泄漏点,可能不完整: 第一个: cJSON *cjson_receive = cJSON_Parse((char *)buffer); 一旦 if (strcmp((char *)buffer, "shutdown") == 0)这里 break 掉,后面的 cJSON_Delete(cjson_receive)就无法执行,内存就这样泄露掉了 第二个: videoSource::Create()调用了两次,第一次问题不大,第二次处理不妥 原因同第一次。如果 input 创建成功但 output 创建失败,那么随后的 continue 会导致 input 指针内存泄漏。 第三个: if (!socket->Send((void *)json_str, strlen(json_str), remoteIP, remotePort)) 同理,break 之前未释放内存 建议上述内存分配都用智能指针包起来,必要时传递析构用的释放函数,就像这样: std::unique_ptr<cJSON, decltype(cJSON_Delete)> cjson_receive{ cJSON_Parse((char *)buffer), cJSON_Delete }; |
15 yolee599 2024 年 5 月 17 日 我的看法,基本和 14 楼的一致,补充一点 socket 还有很多地方没处理好,比如: 1. if (!socket) 的时候应该释放 net 再 return 。 2. if (!socket->Bind("0.0.0.0", 8899)) 的时候应该同时释放 net 和 socket 再 return 。 3. 下面所有的 return 同理。 4. 在程序结束的时候 socket 也应该关闭和释放。 input 和 output ,如果 input 分配成功了,但是 output 分配失败了,在 continue 之前要先释放掉 input 。 其实主要看这几个关键字是否正确处理了之前分配的内存就行:return/break/continue/goto 。 |
16 albert0yyyy OP |
17 nooneanyone 2024 年 5 月 17 日 你如果不差共享指针这点内存和调用性能,就用 shared_ptr make_shared 吧。不要自己 new delete |