win7下堆管理结构分析

书接上文,上次说到gsoap在内存申请失败的情况下,有个bug,申请内存的时候未判断是否成功就直接使用,会造成程序的崩溃。不过,也不能单纯的怪罪到gsoap上,为何我们程序的内存申请失败呢,是内存用光了吗?

还回到原来的崩溃分析,使用!address查看下内存的使用情况,如下图:

win7下堆管理结构分析_第1张图片

从图中看到,可用内存仅剩0.24%,而绝大多数都被heap占用了,足有73.42%,看到这种情况,说明程序中一定有地方new了内存却没及时释放,以至于内存耗尽,引发gsoap那个bug,这样看来,gsoap引起的崩溃,其实也是我们程序自身的问题。

在网上查到的gsoap内存管理方式:采用soap_malloc(用法和malloc一样)方法分配内存, soap_malloc无需释放内存,在gsoap内部采用链表的方式管理分配的内存,在执行soap_destroy(tsoap); soap_end(tsoap)后会自动回收内存。虽然如此,但由于我们的程序有个定时更新的操作,隔一段时间就会去拿一次资源,每次都会分配内存,而soap_end(tsoap)这个函数,只有程序退出的时候才去调用,因此运行时间一久,就容易把内存耗尽,进而引发core dump。

解决方法是:每次调webservice接口后,就调用这两个函数soap_destroy(tsoap); soap_end(tsoap);清理下临时数据。

XP下堆结构在《软件调试》,《windows高级调试》这两本书里已经写的很详尽了,在此不在赘述,重点讲讲win7及后续系统(包括我现在用的win10)堆结构的变化。

1.堆结构变化

xp下堆结构如图,节选自《软件调试》

win7下堆管理结构分析_第2张图片

HEAP和HEAP_SEGMENT结构起始处都有一个8字节的HEAP_ENTRY结构,HEAP结构内包含一个64元素的HEAP_SEGMENT数组,里面记录了各个HEAP_SEGMENT的信息,对于0号段,首先是一个HEAP结构,在这个之后是该段的HEAP_SEGMENT,其余段则最开始便是HEAP_SEGMENT。

而在win7下,则将其统一起来,win7下的HEAP结构,形如

typedef struct _HEAP_ENTRY
{
	UINT SubSegmentCode;
	USHORT PreviousSize;
	BYTE SegmentOffset;
	BYTE UnusedBytes;
}HEAP_ENTRY;

typedef struct _HEAP_SEGMENT

{
	HEAP_ENTRY Entry;
	UINT   SegmentSignature;
	UINT   SegmentFlags;
	LIST_ENTRY SegmentListEntry; //各heap_segment通过此字段连接
	PHEAP Heap;                  //指向所属的heap
	//...省略若干字段
	LIST_ENTRY UCRSegmentList;
}HEAP_SEGMENT;

typedef struct _HEAP

{
	HEAP_SEGMENT Segment;
	UINT   Flags;
	UINT   ForceFlags;
	//...省略若干字段
	LIST_ENTRY SegmentList;  //通过此字段找到各heap_segment,从0号段开始,自然首先同HEAP最开始处那个HEAP_SEGMENT的SegmentListEntry链接
	//...省略若干字段
	HEAP_TUNING_PARAMETERS TuningParameters;
}*PHEAP, HEAP;

可以看出,最大区别是HEAP结构记录HEAP_SEGMENT的方式采用了链表,这样不再受数组大小的约束,同时将HEAP_SEGMENT字段包含进HEAP,这样各堆段的起始便统一为HEAP_SEGMENT,不再有xp下0号段与其他段那种区别,可以统一进行管理了。
2.前端分配器的变化

XP下采用旁视列表(Look Aside List,LAL)前端分配器,旁视列表其实就是一张表,位于0号段HEAP_SEGMENT结构之后,该表有128项,每一项对应于一个单向链表。每个单向链表都包含了一组固定大小空闲堆块,从16字节的大小开始依次递增。当有分配请求到来的时候,即可在对应的列表中进行分配,如果在LAL中无法满足这个请求,则将这个请求转发到后端分配器中做进一步的处理。

而在win7下,则使用低碎片(Low Fragmentation,LF)前端分配器,



你可能感兴趣的:(软件调试)