• 捉虫记(一)GC堆中的“内存泄漏”


    首先介绍一下程序,Server程序,同时有好多Client连接,Client用tcpSocket发送数据给Server,Server对数据进行处理并返回处理结果给Client。虽然整个程序的开发时间很长,但中间不停的需求变更,功能不停地增加减少,代码也是好多人,每人几个模块甚至是几个函数杂凑起来,系统正在被使用,功能也不断被增加,总之......是一个SHZY初级阶段特色的绝对代表的Server程序。
     
    这是我接手这个程序之后的一些Bug的发现和修改,把他记录下来,以做茶余饭后的谈资。
     
    对于这种要文档没文档、要规范没规范程序,要在一边添加新功能,一边发现和修改bug是极其困难的,如果没有一些大杀器,不太好搞,而一但有了一些大杀器,对于有些问题的发现以及解决又简单到令人发指,WinDbg就是这样一种大杀器。

            程序一天部署好几次,没办法,bug太多了,催的又急,每次修改一下就赶紧部署了,某天突然有个经理电话里喊道,你这程序内存怎么越用越大,幸好哥早有防备,呃,经理,先别着急,请双击一下桌面的“Server.Bat”,然后重新启动一下程序(重新启动一下内存就小了哈)。

    1。首先对程序的运行有个大概的了解。(比如说 程序运行了多长时间,内存多大)

    登到服务器上一看,我的天, dump文件1.8G---程序大概是2个小时之前部署

    打开windbg,打开dump,呃,程序运行了2个半小时。

            设置上符号地址,然后加载sos

           2。查看GC堆上的内存占用是多大。(一般来讲,内存泄露都是GC堆上的问题,也就是说new了一些对象在某些情况下没有释放,!EEHeap -GC 正是查看GC堆得命令,我们看到

    最后一行GC Heap Size是1.2G,1.8G的内存,GC堆1.2G,肯定有问题了,当然,Loader堆也不大可能有600M,以后我会对Loader堆继续分析。。。

           第一个命令  !EEHeap -GC

           

            3。确定是GC堆的问题,就看看GC堆上存活的对象是那些。(!dumpheap -stat 就是GC堆上的统计,我们看到统计结果,第一行SingnallingContex,  00000644803111a0  是该对象的MethondTable的地址,而186402是该对象在内存中的个数,35789184是大小,这是纯净的大小,该对象里面所包含的对象的大小不算在内,看到Context相关联的ProcessDetail竟然有3768463个,所占字节为211033928,这足够惊人了。。。 

           呃,看到这里都能猜想到,Loader堆里肯定有很多垃圾,先不管这么多了[我会在后续中介绍Loader堆里面的事情],先看看GC堆里有什么,第二个命令!DumpHeap -stat。

           

           看到一个很熟悉的名字 “SingallingContext”,这是一个Context,这个Context是接收到数据之后包装一下经过业务的pipfilter。1W多个,而Context上多呆的ProcessDetail竟然到了百万,猜测是某个处理Module缓存住了,没有释放。

    4。确定了是哪种类型对象的问题,那就看看该对象。( !Dumpheap -mt 00000644803111a0  你也可以用 !Dumpheap -type SingnallingContex 这个命令

    于是 第三个命令 !Dumpheap -mt 00000644803111a0   

           

           5。找到其中一个对象,看看是因为谁的引用而没有释放(!gcroot +对象地址 这个命令就可以得到这个对象的“根”,这个过程会非常长,具体讲该命令将会遍历所有的根,我们看到Scan Thread 67 实际上就是在扫描67号线程的线程Stack是不是引用了该对象。)

    额,这么多,随便找一个,就找最后一个吧,第四个命令 !gcroot 00000001677ceda8,这个时候该去厕所抽支烟.... 

     

    6。我们找到了“罪魁祸首”的对象,一般我们看看代码就OK了。(这里闲的无聊的话我们可以看看这个对象到底占了多大的内存,!objsize +对象地址,我们就看到了,这个命令时间非常长,长到你可以蹲着抽烟)

    额,看到根了,SingallingContextTracer,这个东西是个跟踪器,不会是跟踪器没有释放吧??先不管,看看多大,第五个命令 !objsize 00000000c1707658 

     

           

    打开代码,找到SingallingContextTracer,呃,这个可怜的家伙把每个Context放到自己的Queue中,然后开启另外的线程来处理,可惜处理线程被谁注释掉了(跟踪功能因为没啥用处,被去掉了,但是去掉了应该不让入队啊),好吧,赶紧修改代码,重新部署......
     

     BTW:贴图排版好累啊,怎样容易排版一些啊?

    -----------------------------------------------------------------------------------------------------

    总结:

    1.对程序大概了解,知己知彼。

    2.确定什么地方引起的泄露,是GC堆还是什么Loader堆等。

    3.确定是哪种类型引起的泄露,其实可以dump两次,然后通过比较两次对象的不同,更加明显看出是那个类型的对象出了问题。

    4.找到该对象。

    5.找到该对象的root。

    root都找到了,剩下的问题就都好解决了。。。。 

            

     
  • 相关阅读:
    Linux零拷贝技术 直接 io
    纯Python模式
    c 越界 数组越界
    哈希 二叉树
    Cache busting
    sub esp
    lisp 代码即数据
    hexdump
    nmap
    对象 闭包
  • 原文地址:https://www.cnblogs.com/StevenChennet/p/2606780.html
Copyright © 2020-2023  润新知