在开发过程中出现内存泄漏反复排查具体原因,终于定位到是因为vector操作不当引起内存泄漏,再次记录此次过程
struct FieldInfo {
std::string value;
std::vector<std::string> multi_value;
bool valid_discrete_feature_ = false;
FieldInfo() {
}
FieldInfo(std::string& single_value) : value(single_value) {
}
};
std::vector<FieldInfo> ad_fields(FLAGS_max_feature_count); (FLAGS_max_feature_count = 1024)
vector主要就是保存上面的结构体,上面结构体相对简单,主要是给value赋值,代码如下:
ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;
(直接使用ad_fields位置 直接赋值给其内部结构体value)
ad_fields向量需要循环引用赋值(具体是业务需要对ad级别特征不断填充、覆盖)
每次对ad_fields填充前进行了clear()操作。
定位到内存泄漏在
ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;
字符串赋值 导致内存不断上涨。但是注释掉这些赋值代码,内存就不在上涨了。
我开始怎么也想不明白,由于使用std::string,感觉字符串不应该产生内存泄漏。(字符串长度越大,内存上涨越快)
查看std::string赋值构造函数(实现如下)
1、本身长度大于拷贝目标的长度,自己拷贝到已指向字符串位置。无需释放,重新申请内存
2、本身长度小于拷贝目标的长度,需要释放到自己内存,在重新申请,之后进行字符串的拷贝。
/**
* 赋值构造函数,调用了assign函数
*/
basic_string& operator=(const basic_string& __str) { return this->assign(__str); }
/**
* 调用了_M_assign函数
*/
basic_string& assign(const basic_string& __str) {
this->_M_assign(__str);
return *this;
}
/**
* 赋值的核心函数
*/
template <typename _CharT, typename _Traits, typename _Alloc>
void basic_string<_CharT, _Traits, _Alloc>::_M_assign(const basic_string& __str) {
if (this != &__str) {
const size_type __rsize = __str.length();
const size_type __capacity = capacity();
/**
* 如果capacity不够用,需要进行重新分配
*/
if (__rsize > __capacity) {
size_type __new_capacity = __rsize;
pointer __tmp = _M_create(__new_capacity, __capacity);
_M_dispose();
_M_data(__tmp);
_M_capacity(__new_capacity);
}
/**
* 将__str指向的内存拷贝到当前对象指向的内存上
*/
if (__rsize) this->_S_copy(_M_data(), __str._M_data(), __rsize);
_M_set_length(__rsize);
}
}
发现string是有内存释放的,不应该是字符串的赋值构造函数原因。
之后开始查看vector,代码如下:
bool _Buy(size_type _Capacity)
{ // allocate array with _Capacity elements
this->_Myfirst() = pointer();
this->_Mylast() = pointer();
this->_Myend() = pointer();
if (_Capacity == 0)
return (false);
else if (max_size() < _Capacity)
_Xlen(); // result too long
else
{ // nonempty array, allocate storage
this->_Myfirst() = this->_Getal().allocate(_Capacity);//申请内存
this->_Mylast() = this->_Myfirst();
this->_Myend() = this->_Myfirst() + _Capacity;//设置end位置,也就是容器capacity 大小
}
return (true);
}
explicit vector(size_type _Count)
: _Mybase()
{ // construct from _Count * value_type()
if (_Buy(_Count))
{ // nonzero, fill it
_TRY_BEGIN
_Uninitialized_default_fill_n(this->_Myfirst(), _Count,
this->_Getal());//默认填充
this->_Mylast() += _Count;//设置last位置,也是就是size大小
_CATCH_ALL
_Tidy();
_RERAISE;
_CATCH_END
}
}
构造函数运行完 我们能够得到capacity = size = 1024 。
前面我们说过每次使用ad特征前都会对vector进行清理。代码如下
ad_fields.clear();//当调用clear函数后 capacity =1024 size=0
void clear() _NOEXCEPT
{ // erase all
this->_Orphan_all();
_Destroy(this->_Myfirst(), this->_Mylast());//会调用类的析构函
this->_Mylast() = this->_Myfirst();//size为空
}
说明调用clear函数并没有对vector内存块进行释放。只不过把vector里的对象都清空了。
这样即使vector的局部对象析构了,也不会在对里头成员进行析构了,因为clear函数已经清空对象了。
之后我们来看上面排查到内存泄漏的位置,对vector内存对象直接操作并进行类成员赋值操作,
ad_fields[FIELD_INDUSTRY_LEVEL1_ID].value = location_result.industry_level1_id;//字符串的复制构造函数
value会根据字符串长短进行销毁自己内部内存申请新内存,但是没有最后一次的局部成员vector析构函数调用,导致string没有释放内存。
希望能对你有一些帮助,任何看是不可能,只要你最求其数源一定会得到你想要的答案。
文章浏览阅读3.2k次,点赞54次,收藏78次。在实现网络爬虫的爬取工作时,就必须使用网络请求,只有进行了网络请求才可以对响应结果中的数据进行提取,urllib模块是python自带的网络请求模块,无需安装,导入即可使用。下面将介绍如果使用python中的urllib模块实现网络请求_with open 保存图片
文章浏览阅读1.2k次。最近在做关于安卓安全方面的东西,需要对底层进行注入,在看雪论坛上看了不少资料,查阅了相关书籍了解了一下,需要用到JNI。利用JNI可以实现Java和底层的C/C++模块相互沟通(NDK的框架用到了该外观设计模式,Facade Pattern) 其具体步骤如下:(1)创建一个包含native本地方法的类;(2)利用javah工具生成C/C++语言的头文件;(3)创建.c源文件,使用C/C++实_jnivlzm
文章浏览阅读485次,点赞9次,收藏6次。RSA算法进行 数据加解密 与 数字签名;_哈希算法与rsa算法
文章浏览阅读3.2k次。HTML、CSS、JS实现轮播图效果:包含分页按钮及切换箭头_html轮播图添加左右箭头
文章浏览阅读707次。Shader定义:官方的定义是这样的: Shader是返回水平跨度颜色的对象的基类,绘图期间。 Shader的子类安装在Paint调用中,paint.setShader(着色器)。 之后的任何对象(除了位图之外)都是使用该绘制绘制将从着色器获取其颜色。它有五个子类,分别是:SweepGradient、LinearGradient、BitmapShader、Compose..._android sweepgradient
文章浏览阅读530次。222_通信emm substate
文章浏览阅读1.6k次。 Intellij idea找不到misc.xml文件,关闭当前项目IDE,重新打开idea, File -> import project,即可重新生成该文件_error:internal error: (java.io.filenotfoundexception)
文章浏览阅读129次。洛谷2814极度细(毒)节(瘤)的字符串处理,以及最后一步弄错。。。。。(水了70分?)【代码~】 #include<bits/stdc++.h>using namespace std;const int MAXN=5e4+10;map<string,int> name;map<int,string> id;map<string...
文章浏览阅读123次。使用JasperReport+iReport进行WEB开发 一、JasperReport和iReport简介: 1、JasperReport简介 JasperReport是一个强大、灵活的报表生成工具,是开放源代码组织sf.net中的...
文章浏览阅读3k次,点赞5次,收藏18次。初入数据结构的哈希表(Hash Table)这次我们来总结一下关于哈希表的知识,首先我们要了解什么是哈希表,哈希函数的构造思路有哪些?怎么解决哈希冲突?最后再去分析一下哈希查找算法。哈希表的概念 前提小知识什么是哈希表?哈希表的四个概念关键字、值、哈希函数、哈希地址、哈希表之间的关系?什么是哈希冲突常见的哈希函数构造方法 怎么样才是好的哈希函数?常见构建哈希函数的六个方法...
文章浏览阅读584次。VS报错 error LNK2005: _DllMain@12 已经在 MSVCRTD.lib(dllmain.obj) 中定义链接报错:错误 33 error LNK2005: _DllMain@12 已经在 MSVCRTD.lib(dllmain.obj) 中定义 E:\客户问题\w_王鹏\EventLibTest_TibrvAlternative_Mult_error lnk2005: _dllmain@12 已经在 msvcrtd.lib(dll_dllmain_stub.obj) 中定义
文章浏览阅读645次。SOAP (Simple Object Access Protocol) 顾名思义,是一个严格定义的信息交换协议,用于在Web Service中把远程调用和返回封装成机器可读的格式化数据。事实上SOAP数据使用XML数据格式,定义了一整套复杂的标签,以描述调用的远程过程、参数、返回值和出错信息等等。而且随着需要的增长,又不得增加协议以支持安全性,这使SOAP变得异常庞大,背离了简单的初衷。另一方面,各个服务器都可以基于这个协议推出自己的API,即使它们提供的服务及其相似,定义的API也不尽相同,这又导致.._soap是指什么