Archive

Archive for October, 2011

内存泄露 (续)

October 29th, 2011 No comments

  在现场的一个服务有巨大的内存泄露的问题,最近一直都在检查这个问题。虽然看起来还有一点泄露,不过已经能够让现场可以撑得更久一点了。

  现场的一个播流的服务(实际上时一个速率可控的http下载的服务器端),由于前一段时间性能不能达到客户的要求,而大幅修改过很多的代码。而且又需要立马上线,结果不能很好的测试就把匆忙修改的程序部署到了现场。现在想起来真实悲催啊,因为性能问题而修改累了一段时间,现在有需要给这个没有充分测试的程序做扫尾动作。

  当现场报说有大量内存泄露以后,我就开始着手使用分析工具来看看泄露到底是什么地方引起的。 我选择了google performance tool来查看内存泄露的点. 安装google perf tool测试一段时候,根据google perftool的提示和code review找到了一些内存泄露的代码。但是奇怪的就是修复了这些泄露点以后重新build一个版本做压力测试发现还是有内存泄露。这个让我觉得很是奇怪,再次使用google perftool测试,但是该工具没有报告任何的内存泄漏点,这个让我一度怀疑这个工具是有问题的。不过后来一想,我们的应用用到了很多的stl class,特别是std::map std::list等东西, 如果这些实例析构以后所占用的内存会被释放,而google perftool是要在程序退出以后才能报告是否有内存泄露。如果一个程序在运行的过程中不断的向一个list或者map插入新的记录但是忘了删除的话,这个依然是一个内存泄漏点。为了验证我的想法,我在google perftool的代码加入了一个signal的响应函数,当接收到这个siganl的时候就做一次内存泄露报告。然后使用这个修改后的google perftool进行测试,程序在大压力的环境下跑了半个小时以后,把所有的测试客户端都停掉(防止在report memory leak的时候还有代码需要做内存分配/释放操作)。然后查看内存泄露报告,发现确实如我所想,内存泄露大都来之std::map中的record没有删除而造成的。

  当我以为这些内存泄露点都修复好了程序就应该没有什么问题了,接下来的压力测试让我觉得有点摸不着头脑了。程序在大压力的情况下跑了48+小时后发现有400M+的内存泄露,停掉所有的客户端以后内存使用并不会回到最初状态。而在此使用google perftool进行检测却找不到有内存泄漏点。迷惑了很长时间以后google了一番找到了http://www.nosqlnotes.net/archives/105这篇文章,也就是GLIBC的内存分配/释放的缺省行为可能造成内存不会被正常交还给kernel(当然我的程序是否是这个问题还需要进一步确认)。

  查找完内存泄露点以后觉得很有必要做一个记录,也好给自己做一个提醒。

1.  malloc / new 以后没有free/delete。

2.  加入到map list vector等容器中的record没有及时删除

3.  智能指针的循环引用,例如A和B是两个实力,其中A有一个智能指针指向B,而B也有一个智能指针指向A。如果不主动调用A或者B的资源释放方法的话A和B均不会被析构

4.  还有就是没有考虑到所有的资源释放情形,导致在特殊情况下资源释放的方法不能被调用到

     例如,在调用了epoll_ctl(…, EPOLL_CTL_ADD , …)以后没有检查返回值,在某个情况下这个函数可能会返回错误,这个时候就应该调用资源释放函数(这个socket已经不可用了)

5. 还有啥?想到了再补充一下…

Categories: programming Tags: ,

内存泄露啦

October 19th, 2011 No comments

内存泄露真的是一个很令人纠结的问题,于是使用了诸如智能指针的东西来避免这个问题,但悲哀的是就连这样还是造成了内存泄露。仔细查看源代码以后发现了问题的所在。

程序使用的智能指针是基于引用次数的,也就是说当某一个object被引用的次数>0的时候,这个object就会保留在内存中。当引用次数减少到0的时候,也就是没有任何一个地方在使用这个object的时候这个object就会被删除。

在程序中有一个地方对于这个智能指针的使用不当导致了引用次数不能正常减少到0,引起了内存泄露。程序中有A和B两个对象,其中B是A的一个member。同时B有一个智能指针指向A(需要在运行的时候使用A的部分功能)。在一切正常的情形下面,当调用A的close的时候,A会调用B的close,同时B会将指向A的智能指针置空(也就是不指向A了),这个时候就是正常的不会产生内存泄露。但是异常的情况下,A的close调用B的close的时候,B不会清除对A的引用,这个时候由于A还有人引用–B还有一个指针指向A,所以A不会被析构,导致了内存的泄露。

这个问题提醒我即便使用了智能指针也要小心的使用,及时清除对对象的引用。还有就是最好不要交叉引用–A引用B,同时B引用A。

Categories: programming Tags: ,

在三亚,喝了点酒,头有点晕

October 17th, 2011 No comments

感觉自己做的东西总是做不成功,也许这就是黎明前的最黑暗的时刻,扛过去了就好了。曾经想过放弃,但是我已经花费了很大的功夫,现在放弃实在是得不偿失啊。那就这样吧,硬着头皮做下去,除非证明根本做不出来再退出吧

Categories: life Tags:

想考驾照…

October 12th, 2011 No comments

  想考驾照,咨询了一下同事。得到一个说法是10月1号以后,非上海户籍需要多交1K多,而且驾校里面每一辆车一个月只能带一个非上海户籍的学生。很明显,这样的政策使得我这类人学车的费用又提升了一档,而且还很难有机会报名成功。

  这都是什么事啊…  连考个驾照都这么的…

Categories: mutter Tags:

终于搭建好了一个自己的blog

October 12th, 2011 No comments

以前使用的是GAE的免费空间来搭建自己的blog,并且绑定了一个从godaddy购买的域名。但是在国内访问gae空间还是太慢,有的时候根本无法访问。于是心一狠在linode上购买了一个VPS。以后就用这个了

Categories: mutter Tags:

openCV中kmeans++的实现

October 9th, 2011 No comments

上次看了一下OpenCV中kmeans算法的实现,现在来看看剩下部分的kmeans++的东西。kmeans++其实是在初始化cluster中心点的时候用到的算法。按照wiki上的说法这样做的好处解决了kmeans的一个问题: the approximation found can be arbitrarily bad with respect to the objective function compared to the optimal clustering.

下面是源代码

centers[0] = (unsigned)rng % N;//随机选择一个数据作为第一个center的中心点
for( i = 0; i < N; i++ )
{
    dist[i] = distance(data + step*i, data + step*centers[0], dims);//计算每一个数据点到第一个center的中心的距离
    sum0 += dist[i];//sum of the these distance
}
for( k = 1; k < K; k++ )//计算剩下的centers的中心点
{
    double bestSum = DBL_MAX;
    int bestCenter = 1;
    for( j = 0; j < trials; j++ )//在这里trials通常是3,原因我不清楚
    {
        double p = (double)rng*sum0, s = 0;//
        for( i = 0; i < N1; i++ )
            if( (p = dist[i]) <= 0 )
                break;//随机选择一个数据点,这个数据点貌似有什么特性,不过我没有看出来,为啥要这样做我还是不清楚
        int ci = i;//我们暂时找到了一个新的中心点
        for( i = 0; i < N; i++ )
        {
            tdist2[i] = std::min(distance(data + step*i, data + step*ci, dims), dist[i]);
            s += tdist2[i];//计算每个点到新的这个中心点的距离
        }
        
        if( s < bestSum )
        {//如果这个新的中心点比刚才找到的要好的话,就用这个了
            bestSum = s;
            bestCenter = ci;
            std::swap(tdist, tdist2);
        }
    }
    centers[k] = bestCenter;//这样查找3次以后我们得到了一个新的center的中心点
    sum0 = bestSum;
    std::swap(dist, tdist);
}
for( k = 0; k < K; k++ )
{//输出
    const float* src = data + step*centers[k];
    float* dst = _out_centers.ptr<float>(k);
    for( j = 0; j < dims; j++ )
        dst[j] = src[j];
}

虽然上面的代码很简单,但是因为没有理解算法所以不能完全搞懂。还需要一边看看算法一边在琢磨一下每一步在做什么和为什么要这么做。

Categories: programming Tags: , ,

OpenCV中kmean算法的实现

October 7th, 2011 No comments

  我自己的Kmean算法的实现看起来是有大的问题了,虽然不能从代码中看出问题的所在,不过生成图像说明了一切,一个有蓝天白云的图片经过我的cluster算法以后白云居然不见了。我暂时还没有找到问题的所在。查找这种纯数据运算的算法bug真的是很费劲。这些天又在看OpenCV,于是就打算看看OpenCV里面的KMean算法的具体实现,在这里记录一下我的理解,以备查验。

  我的OpenCV的版本是2.3.1. 其中Kmean的实现在modulescoresrcmatrix.cpp里面,所有的实现都在

double cv::kmeans( InputArray _data, int K,  InputOutputArray _bestLabels, TermCriteria criteria, int attempts, int flags, OutputArray _centers ) 这个函数里面。

当然,其实我调用的函数是static double cppKMeans()这个函数,而这个函数只是简单的检查一下参数,最终还是会调用到 cv::kmeans(…)这个函数中去得

cv::kmeans()这个函数的参数对于我这种OpenCV的初学者来说也是一种很痛苦的东西,瞎弄了半天才搞清楚是怎么一回事。

_data: 这个就是你要处理的数据,例如是一个CvMat数据

K : 你需要最终生成的cluster的数量

_bestLabels: 当cv::kmeans执行完毕以后, _bestLabels里面储存的就是每一个对应的数据元素所在的cluster的index,这样你就可以更新你的数据了

criteria: 这个东西是用来告诉cv::kmeans以一个什么样的停止条件来运行,例如criteria.epsilon = 0.01f;criteria.type = CV_TERMCRIT_EPS; 这个表示centers在两轮cluster运行以后的距离差,如果这个距离小于等于criteria.epsilon就停止返回当前得到的centers,否则继续

attempts: 最多尝试多少次,文档上说一般设置为2

flags: 这个主要是传递一些配置参数,例如 初始的时候使用user code给定的label –KMEANS_USE_INITIAL_LABELS,使用kmeans++初始化算法– KMEANS_PP_CENTERS

_centers: 这个就是我们想要的结果了,该函数运行完毕以后,这个变量里面储存所有的center的数据,也就是你想要的东西了

下面我们来看看kmeans的具体实现。

前面部分的参数初始化和检验之类的就略过了,直接进入算法部分。

    for( a = 0; a < attempts; a++ )
    {//尝试多少次,一般为2
        double max_center_shift = DBL_MAX;
        for( iter = 0; iter < criteria.maxCount && max_center_shift > criteria.epsilon; iter++ )
        {//退出循环的条件是达到最大的循环次数criteria.maxCount 或者 center的差小于等于criteria.epsilon
            swap(centers, old_centers);//保存centers的数据到old_centers中去
 
            if( iter == 0 && (a > 0 || !(flags & KMEANS_USE_INITIAL_LABELS)) )
            {//如果是第一次运行且不使用user code提供的labels,那就由该算法提供center的初始化
                if( flags & KMEANS_PP_CENTERS )
                    generateCentersPP(data, centers, K, rng, SPP_TRIALS);//用kmeans++算法来初始化center,这个我没有没有搞懂是咋回事,略过
                else
                {
                    for( k = 0; k < K; k++ )
                        generateRandomCenter(_box, centers.ptr<float>(k), rng);
                }
            }
            else
            {
                if( iter == 0 && a == 0 && (flags & KMEANS_USE_INITIAL_LABELS) )
                {//如果是user code提供的labels,需要检查一下数据是否有效
                    for( i = 0; i < N; i++ )
                        CV_Assert( (unsigned)labels[i] < (unsigned)K );
                }
            
                // compute centers
                centers = Scalar(0);//不知道Scalar是个什么东西,不过可以猜到这个目的是为了将centers中的数据置为0,希望没有猜错
                for( k = 0; k < K; k++ )
                    counters[k] = 0;//同时清空所有center中数据的个数
 
                for( i = 0; i < N; i++ )
                {//遍历所有的数据
                    sample = data.ptr<float>(i);//取得当前需要的处理的数据
                    k = labels[i];//得到当前数据所在center的index
                    float* center = centers.ptr<float>(k);//取得centers[k]的数据
                    for( j = 0; j <= dims  4; j += 4 )//这个我着实没有搞懂是为啥,完全不理解,不过我的数据维度是3,这个部分完全不会被执行到,那就暂时放过它吧
                    {
                        float t0 = center[j] + sample[j];
                        float t1 = center[j+1] + sample[j+1];
 
                        center[j] = t0;
                        center[j+1] = t1;
 
                        t0 = center[j+2] + sample[j+2];
                        t1 = center[j+3] + sample[j+3];
 
                        center[j+2] = t0;
                        center[j+3] = t1;
                    }
                    for( ; j < dims; j++ )
                        center[j] += sample[j];//把每一个维度的数据都加到center中去
                    counters[k]++;//同时更新该center的数据个数
                }
 
                if( iter > 0 )
                    max_center_shift = 0;
 
                for( k = 0; k < K; k++ )
                {
                    float* center = centers.ptr<float>(k);
                    if( counters[k] != 0 )
                    {//如果该center有数据的话,就计算一下这些数据的平均值,以便得到centroid数据
                        float scale = 1.f/counters[k];
                        for( j = 0; j < dims; j++ )
                            center[j] *= scale;
                    }
                    else
                        generateRandomCenter(_box, center, rng);//这个是要干什么?如果有center中没有数据就要推到重来吗?没有搞懂,以后再说吧
                    
                    if( iter > 0 )
                    {//计算一下新旧center的centroid的差这个东西将会被作为循环停止的判断条件之一
                        double dist = 0;
                        const float* old_center = old_centers.ptr<float>(k);
                        for( j = 0; j < dims; j++ )
                        {
                            double t = center[j]  old_center[j];
                            dist += t*t;
                        }
                        max_center_shift = std::max(max_center_shift, dist);
                    }
                }
            }
 
            // assign labels
            compactness = 0;
            for( i = 0; i < N; i++ )
            {//现在开始决定每一个数据到底该扔到哪一个center中
                sample = data.ptr<float>(i);
                int k_best = 0;
                double min_dist = DBL_MAX;
 
                for( k = 0; k < K; k++ )
                {
                    const float* center = centers.ptr<float>(k);
                    double dist = distance(sample, center, dims);
 
                    if( min_dist > dist )
                    {
                        min_dist = dist;
                        k_best = k;
                    }
                }//这个循环计算了当前的这个数据离哪一个center的centroid最近,
 
                compactness += min_dist;
                labels[i] = k_best;//这个数据就扔到哪个距离最近的center中去吧
            }
        }
 
        if( compactness < best_compactness )
        {//找到更好的center数据,用之
            best_compactness = compactness;
            if( _centers.needed() )
                centers.copyTo(_centers);
            _labels.copyTo(best_labels);
        }
    }
 

看完OpenCV的代码,我的感觉是我的代码和它的相差不是很大,但是为何用我的代码生成的图像就不正确呢,而用OpenCV的kmeans算法得到的图像就非常接近原图。 看来还得继续看看我的代码哪里错误,或者我直接使用OpenCV的kmeans算法实现算了

Categories: programming Tags: , ,