C++新手,求教为啥程序跑起来的内存越用越多 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
albert0yyyy
V2EX    C++

C++新手,求教为啥程序跑起来的内存越用越多

  •  
  •   albert0yyyy 2024 年 5 月 16 日 4261 次点击
    这是一个创建于 705 天前的主题,其中的信息可能已经有所发展或是发生改变。

    感觉好像是哪个变量申请了内存没有释放,然后越用越多,大概 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; } 
    17 条回复    2024-05-17 19:06:45 +08:00
    momo1999
        1
    momo1999  
       2024 年 5 月 16 日   1
    你都知道你写的是 C++了,多用 RAII 自动释放啊,手动释放太难了。
    YsHaNg
        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.
    albert0yyyy
        3
    albert0yyyy  
    OP
       2024 年 5 月 16 日
    @YsHaNg gpt 我问了,好像并不能解决.

    有的指针释放了会报错提示已经释放过了,不能再次释放了。所以我就注释了一些会报错的释放操作
    albert0yyyy
        4
    albert0yyyy  
    OP
       2024 年 5 月 16 日
    @shuax 我去看看 RAII ,感谢
    YsHaNg
        5
    YsHaNg  
       2024 年 5 月 16 日   1
    @albert0yyyy RAII+smart ptr 多用 auto 会让生活好很多
    albert0yyyy
        6
    albert0yyyy  
    OP
       2024 年 5 月 16 日
    @YsHaNg #5 感谢,我去看看。
    watzds
        7
    watzds  
       2024 年 5 月 16 日
    内存越用越多也正常啊,看能增加到多少
    hello2090
        8
    hello2090  
       2024 年 5 月 16 日
    你实在调不出来的话,code 一部分一部分的 comment 掉试呗
    bfjm
        9
    bfjm  
       2024 年 5 月 16 日
    1. 可以使用 valgrind 看看哪里会有内存泄漏
    2. 看这个 cJSON 的情况是分配了内存,需要手动调用他的内存分配函数,你可以使用 RAII 封装一下
    类似这种
    ```c++
    class smart_ptr
    {
    smart_ptr(char *buffer)
    {

    }

    ~smart_ptr()
    {

    }

    };


    ```
    bfjm
        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
    bfjm
        11
    bfjm  
       2024 年 5 月 16 日
    另外你先得了解清除,这里面谁分配了内存,(谁分配谁释放),分配的是堆内存还是栈内存,栈内存不用手动释放,堆内存需要手动释放
    bfjm
        12
    bfjm  
       2024 年 5 月 16 日
    我不太确定 cJSON *cjson_obj = cJSON_CreateObject(); 这里分配内存没有,这里会比较可疑
    sidyhe
        13
    sidyhe  
       2024 年 5 月 16 日
    查代码比较难的话, 用 tcmalloc 检测泄露, 它会告诉你哪里的内存没有释放
    cnbatch
        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 };
    yolee599
        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 。
    albert0yyyy
        16
    albert0yyyy  
    OP
       2024 年 5 月 17 日
    @watzds
    @hello2090
    @bfjm #9

    @sidyhe
    @cnbatch
    @yolee599


    谢谢各位哥哥们,学到了很多 C++的技巧,我去改改。回头给你们反馈
    nooneanyone
        17
    nooneanyone  
       2024 年 5 月 17 日
    你如果不差共享指针这点内存和调用性能,就用 shared_ptr make_shared 吧。不要自己 new delete
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5632 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 60ms UTC 06:59 PVG 14:59 LAX 23:59 JFK 02:59
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86