SrsAutoFree模式,避免内存泄漏和错误

来源:互联网 发布:js中的原型对象 编辑:程序博客网 时间:2024/05/08 09:20

C/C++中内存是一个很难处理的事情,正如强项就是弱项,强大的地方也是致命的地方。

内存不释放就会泄漏,多次释放就会段错误,越界更恐怖。

不释放和多次释放都可以用SrsAutoFree规避,越界就需要工具和经验的问题。

释放和多次释放,原因是内存或者对象的生命周期过程,譬如在一个while循环中,有些时候要释放,有些时候continue就好,就容易出问题。

真的需要活N久的对象吗?很少。大部分的作用域在当前函数和子函数,局部变量就可以搞定,是的,有些时候就是没有办法用局部变量,譬如由一个函数收取的包,在当前函数就需要释放。

考虑下面的逻辑:

int SrsClient::publish(SrsSource* source, bool is_fmle){int ret = ERROR_SUCCESS;SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER);while (true) {// switch to other st-threads.st_usleep(0);SrsCommonMessage* msg = NULL;if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) {srs_error("recv identify client message failed. ret=%d", ret);return ret;}SrsAutoFree(SrsCommonMessage, msg, false);pithy_print.set_age(msg->header.timestamp);// reportableif (pithy_print.can_print()) {srs_trace("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", (int)srs_get_system_time_ms(), pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps());}// process audio packetif (msg->header.is_audio() && ((ret = source->on_audio(msg)) != ERROR_SUCCESS)) {srs_error("process audio message failed. ret=%d", ret);return ret;}// process video packetif (msg->header.is_video() && ((ret = source->on_video(msg)) != ERROR_SUCCESS)) {srs_error("process video message failed. ret=%d", ret);return ret;}// process onMetaDataif (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {if ((ret = msg->decode_packet()) != ERROR_SUCCESS) {srs_error("decode onMetaData message failed. ret=%d", ret);return ret;}SrsPacket* pkt = msg->get_packet();if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) {srs_error("process onMetaData message failed. ret=%d", ret);return ret;}srs_trace("process onMetaData message success.");continue;}srs_trace("ignore AMF0/AMF3 data message.");continue;}// process UnPublish event.if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) {if ((ret = msg->decode_packet()) != ERROR_SUCCESS) {srs_error("decode unpublish message failed. ret=%d", ret);return ret;}// flash unpublish.if (!is_fmle) {srs_trace("flash publish finished.");return ret;}SrsPacket* pkt = msg->get_packet();if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {SrsFMLEStartPacket* unpublish = dynamic_cast<SrsFMLEStartPacket*>(pkt);return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id);}srs_trace("ignore AMF0/AMF3 command message.");continue;}}return ret;}
从RTMP协议栈拿到包后,使用在这个作用域一定会释放,所以使用AutoFree就可以保证只释放一次,而且一定释放一次。

AutoFree实现很可靠,用C++的构造和析构,以及宏定义就可以搞定。


对比以下代码,没有使用auto free模式:

// ignore when no messages.int count = (int)msgs.size();if (msgs.empty()) {        continue;}// all msgs to forward.int i = 0;for (i = 0; i < count; i++) {        SrsSharedPtrMessage* msg = msgs[i];        msgs[i] = NULL;        // we erased the sendout messages, the msg must not be NULL.        srs_assert(msg);                ret = client->send_message(msg);        if (ret != ERROR_SUCCESS) {                srs_error("forwarder send message to server failed. ret=%d", ret);                // convert the index to count when error.                i++;                                break;        }}// clear sendout mesages.if (i < count) {        srs_warn("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret);} else {        srs_info("clear forwarded msg, total=%d, forwarded=%d, ret=%d", count, i, ret);}msgs.erase(msgs.begin(), msgs.begin() + i);if (ret != ERROR_SUCCESS) {        break;}

使用auto free模式后:

// ignore when no messages.if (count <= 0) {    srs_verbose("no packets to forward.");    continue;}SrsAutoFree(SrsSharedPtrMessage*, msgs, true);// all msgs to forward.for (int i = 0; i < count; i++) {    SrsSharedPtrMessage* msg = msgs[i];        srs_assert(msg);    msgs[i] = NULL;        if ((ret = client->send_message(msg)) != ERROR_SUCCESS) {        srs_error("forwarder send message to server failed. ret=%d", ret);        return ret;    }}

如下图:


原创粉丝点击