[转载]使用 Lookaside List 分配内存

1. 概述

windows 提供了一种基于 lookaside list 的快速内存分配方案,区别于一般的使用 ExAllocatePoolWithTag() 系列函数的内存分配方式。每次从 lookaside list 里分配 fixed size 的内存。 系统构建两个条 lookaside 链表:ExNPagedLookasideListHead 和 ExPagedLookasideListHead,分别用于 Non-paged 内存和 paged 内存。

lookaside list 链表头是 LIST_ENTRY 结构,因此:整个 lookaside list 链是双向链表。ExNPagedLookasideListHead 和 ExPagedLookasideListHead 是 kernel 的全局变量,由系统进行初始化。

下图是由 ExNPagedLookasideListHead 链头构造的 non-paged lookaside list 示意图:

在使用 ExInitializeNPagedLookasideList() 函数进行初始化 NPAGED_LOOKASIDE_LIST 结构(或 PAGED_LOOKASIDE_LIST 结构)时,将节点的 LIST_ENTRY 域挂接在链表头里,形成一条双向链表。

2. lookaside list 节点结构

lookaside list 的节点是 NPAGED_LOOKASIDE_LIST 结构(用于 non-paged 链)或 PAGED_LOOKASIDE_LIST(用于 paged 链),它们的定义如下

typdef struct _NPAGED_LOOKASIDE_LIST
{
GENERAL_LOOKASIDE L;
KSPIN_LOCK Lock;
} NPAGED_LOOKASIDE_LIST, *PNPAGED_LOOKASIDE_LIST;
 
 
typdef struct _PAGED_LOOKASIDE_LIST
{
GENERAL_LOOKASIDE L;
FAST_MUTEX Lock;
} PAGED_LOOKASIDE_LIST, *PPAGED_LOOKASIDE_LIST;

内部的 GENERAL_LOOKASIDE 子结构维护着 lookaside 的整个工作,下面是 GENERAL_LOOKASIDE 结构的定义:

typedef struct _GENERAL_LOOKASIDE {
SLIST_HEADER ListHead;// 单向链表,用来挂接 pool block
USHORT Depth;// lookaside 的 depth,缺省为 4
USHORT MaximumDepth;// maximum depth,缺省为 256
ULONG TotalAllocates;                 // 记录进行多少次分配
ULONG AllocateMisses;               // 记录分配从 lookaside list 中分配失败次数
ULONG TotalFrees;                   // 记录进行多少次释放
ULONG FreeMisses;                   // 记录从 lookaside list 中释放 miss 次数
POOL_TYPE Type;                     // pool type 类型,与 ExAllocatePoolWithTag() 参数一致
ULONG Tag;                                  // 用于 ExAllocatePoolWithTag()
ULONG Size;                                 // 用于 ExAllocatePoolWithTag()
PALLOCATE_FUNCTION Allocate;       // 自定义的分配函数,缺省使用 ExAllocatePoolWithTag()
PFREE_FUNCTION Free;                 // 自定义的释放函数,缺省使用 ExFreePool()
LIST_ENTRY ListEntry;                 // 双向 link list
ULONG LastTotalAllocates;           // 记录上一次 Allocate 次数
ULONG LastAllocateMisses;         // 记录上一次 Allocate  miss 次数
ULONG Future[2];                            // 保留
} GENERAL_LOOKASIDE, *PGENERAL_LOOKASIDE;

每个节点有两条链表:

  • 单向链表:用于挂接 pool 块,这些内存块就是从系统 pool 里分配而来的(non-paged 内存或 paged 内存)。
  • 双向链表:用于挂接在整个 lookaside list 里,也是就前面所说的 ExNPagedLookasideListHead 链表头和 ExPagedLookasideListHead 链表头。

在前面描述图的基础上,形成像下面的结构:

因此:每个节点都有一条 SLIST_ENTRY 链,挂接着内存块!,这些内存链是在释放 lookaside list 时挂接上去的。在初始化时,这些内存块链是不存在的!

假如:Lookaside 指向其中一个 NPAGED_LOOKASIDE_LIST 节点 有三个 Depth 值是需要分清楚的:

  1. 节点当前的深度:Lookaside->L.ListHead.Depth 它指示节点当前有多少个内存块挂接着。
  2. 节点的最小深度:Lookaside->L.Depth 它指示着每个节点最少以挂接多少个内存块。在缺省的情况下,这个 Depth 值为 4,最少可以挂接 3 个内存块。
  3. 节点的最大深度:Lookaside->L.MaximumDepth 它指示节点最大可以挂接多少个内存块。这个值为 256,也就是最多可以挂接 255 个。

在缺省的时候,每个节点的 Depth(深度)的为 4,那么 kernel 根据使用情况来动态修改这个 Depth 值,最大值为 MaximumDepth。因此:Depth 相当于一个“额度”的概念!

3. 节点内存块 size

在一条 lookaside list 链里,每个节点可以有不同 size 的内存块,如下图所示:

在图中的 lookaside 链里,其中 3 个节点分别挂接着 1K,2K 以及 4K 大小的内存块,当需要分配 1K 内存时,从第1个节点里分配。如下面代码所示:

PVOID p = ExAllocateFromNPagedLookasideList(lookaside1);// 从第 1 个节点分配 1K 内存块

在初始化每个节点时,可以为每个节点的内存块 size 设置不同的值,因此:整个 lookaside list 可以挂接着不同 size 的内存块,软件根据需要从不同 size 的节点里分配内存。

4. 初始化节点

在使用 lookaside list 之前,必须构建和初始化一个 NPAGED_LOOKASIDE_LIST 结构(或 PAGED_LOOKASIDE_LIST 结构用于 paged 内存),如下面代码所示:

NPAGED_LOOKASIDE_LIST Lookaisde;// 节点结构
 
//
// 下面初始化节点
//
 
ExInitializeNPagedLookasideList(
&Lookaside,// 节点地址
NULL,// 使用缺省的 Allocate() 例程
NULL,// 使用缺省的 Free() 例程
0,// Flags 保留
1024,// 1K size
‘tag1‘,// Tag
0// Depth 值,未使用
);

在对节点初始化完成后,将节点挂接在以 ExNPagedLookasideListHead 为表头的双向链表里,组织成一个 lookaside list 体系。

下面的 ExInitializeNPagedLookasideList() 例程 C 代码是从 windows 2003 里逆出来的:

VOID ExInitializeNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside,
IN PALLOCATE_FUNCTION Allocate,
IN PFREE_FUNCTION  Free,
IN ULONG  Flags,
IN SIZE_T  Size,
IN ULONG  Tag,
IN USHORT  Depth
)
{
PGENERAL_LOOKASDIDE  L = &Lookaside->L;
 
//
// 初始化  SLIST_HEADER
//
L->ListHead.Next = NULL;
L->ListHead.Depth = 0;
L->ListHead.Spquence = 0;
 
L->Depth = ExMinimulLooasideDepth;// depth 最小值,为 4
L->Type = NonPagedPool | Flags;
L->Tag = Tag;
L->Size = Size;
L->MaximumDepth = 256;// depth 最大值, 256
L->TotalAllocates = 0;
L->AllocateMisses = 0;
L->TotalFrees = 0;
L->FreeMisses = 0;
 
if (Allocate == NULL)
{
L->Allocate = ExAllocatePoolWithTag;
}
else
{
L->Allocate = Allocate;
}
 
 
if (Free == NULL)
{
L->Free = ExFreePool;
}
else
{
L->Free = Free;
}
 
L->LastTotalAllocates = 0;
L->LastAllocateMisses = 0;
 
//
// 将 &L->ListEntry 挂接在 ExNPagedLookasideListHead 链表头里
//
ExfInterlockedInsertTailList(&ExNPagedLookasideListHead, &L->ListEntry, &ExNPagedLookasideLock);
}

从这个代码里,我们可以清楚地看到,节点的单向链表是空的,Depth 和 MaximumDepth 分别是 4 和 256,在对 non-paged 节点初始化时,Flags 值为 NoPagedPool 类型。

对 paged 节点的初始化例程与 non-paged 节点结构是一致的,只是使用了不同的 Flags 值和链表头,下面是 ExInitializePagedLookasideList() 的代码:

VOID ExInitializePagedLookasideList(
IN PPAGED_LOOKASIDE_LIST Lookaside,
IN PALLOCATE_FUNCTION Allocate,
IN PFREE_FUNCTION  Free,
IN ULONG  Flags,
IN SIZE_T  Size,
IN ULONG  Tag,
IN USHORT  Depth
)
{
....
 
L->Type = PagedPool | Flags;// PagedPool 类型
 
...
 
//
// 将 &L->ListEntry 挂接在 ExPagedLookasideListHead 链表头里
//
ExfInterlockedInsertTailList(&ExPagedLookasideListHead, &L->ListEntry, &ExPagedLookasideLock);
}

代码的最后,使用 ExfInterlockedInsertTailList() 例程将节点挂在双向链表里(non-paged 或 paged 类型)

下面代码是从 windows 2003 里逆出来的:

PLIST_ENTRY
ExfInterlockedInsertTailList(
PLIST_ENTRY ListHead,// 双向链表头
PLIST_ENTRY ListEntry,// 双向节点
PKSPIN_LOOK Look// 保留接口
)
{
DISABLEINTERRUPT();// 关中断
 
PLIST_ENTRY Blink = ListHead->Blink;
 
// 
// 下面将节点挂接在尾部
//
ListEntry->Flink = ListHead;
ListEntry->Blink = Blink;
ListHead->Blink = ListEntry;
Blink->Fink = ListEntry;

ENABLEINTERRUPT();// 开中断
 
//
// 空 list
//
if (Blink == ListHead)
{
return NULL;
}

return ListHead;
}

5. 分配内存

软件使用 ExAllocateFromNPagedLookasideList() 例程从 non-paged 链的节点里分配 non-paged 内存块,使用 ExAllocateFromPagedLookasideList() 例程从 paged 链的节点里分配 paged 内存块。

如下面代码所示:

PVOID p = ExAllocateFromNPagedLookasideList(lookaside);// 从 non-paged 链节点里分配内存,返回内存块到 p

在从节点里分配内存块时遇到两种情形:

  1. 当节点的内存块链为空时,直接调用节点的 Allocate 例程来在 pool 里分配内存,此时将记录一次 AllocateMisses
  2. 当节点的内存块链不为空时,将重复使用节点内存块,无需直接分配内存。

当节点挂接着内存块时(不为空),将返回节点地址(SLIST_ENTRY 结构地址),节点为空时将返回内存块地址(如果分配成功的话)。

如上图所示:有两个节点 A 和 B,从节点 A 分配内存将得到节点地址(作为内存块使用),从节点 B 分配将记录一次 miss(从 lookaside 中分配失败),直接调用 Allocate 例程分配内存。

节点挂着的内存块从哪里来?答案是:释放之前分配的内存块时挂接
      当使用 ExFreeToNPagedLookasideList() 例程释放内存块时,在 Depth 允许的情况下,kernel 将内存块挂接在节点内存块链里,以备以续的分配重复使用,而无需进行物理的分配内存操作。
      因此:当节点内存块链为空时,将产生 miss 情形。表示从节点内存块链里分配不到内存。

当返回节点地址时,它属于 SLIST_ENTRY 指针值。而实际上,这个指针值虽然指向 SLIST_ENTRY 结构,但是无需理会类型,它被当作一个空的内存块使用。这是因为:SLIST_ENTRY 结构里只有一个成员,就是 Next 指针(指向下一条 SLIST_ENTRY 结构)。

当返回这个 SLIST_ENTRY 结构指针值的,它里面的 Next 成员被作为“脏”数据而忽略这个数据(作为空的内存块)。

下面我们来看看 ExAllocateFromNPagedLookasideList() 例程的实现,下面代码是从 windows 2003 里逆出来的:

PVOID ExAllocateFromNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST Lookaside
)
{
PSLIST_ENTRY ListEntry;

//
//  TotalAllocates  记录分配的次数,包括成功和失败
//
Lookaside->L.TotalAllocates++;

//
// 从节点里读取内存块链
//
ListEntry = ExInterlockedPopEntrySList(&Lookaside->L.ListHead);
 
//
// 分配内存有两个情形:
// 1. 返回 lookaside list 节点(SLIST_ENTRY) 
// 2. 返回内存,假如 lookaside list 为空
//
if (ListEntry == NULL)
{
//
//  AllocateMisses 记录从 lookaside list 中分配失败的次数
// 也就是从 pool 中分配的次数
//
Lookaside->L.AllocateMisses++;
 
//
// 调用 lookaside list 节点中的 Allocate() 例程
//
return Lookaside->L.Allocate(Lookaside->L.Type, 
Lookaside->L.Size, 
Lookaside->L.Tag);
}
else
return ListEntry;
}

代码使用 ExInterlockedPopEntrySList() 例程来读取节点内存块地址,如果为空时就分配物理内存块,记录一次 miss 事件!这个 miss 事件将被用作 kernel 定期维护和调整 lookaside 链的 Depth 值之用! 根据 miss 次数在分配操作数的频率而提升链的 Depth,达到更好的性能。

对于 paged 类型的内存分配,它看起来像这样:

#define ExAllocateFromPagedLookasideList(Lookaside) \
ExAllocateFromNPagedLookasideList((PNPAGED_LOOKASIDE_LIST)Lookaside)

ExAllocateFromPagedLookasideList() 实际上也是调用 ExAllocateFromNPagedLookasideList() 例程,只不过传递的是 PAGED_LOOKASIDE_LIST 节点!

6. 释放内存

使用 ExFreeToNPagedLookasideList() 例程来释放 non-paged 链的内存块,而 ExFreeToPagedLookasideList() 释放 paged 链的内存块。当 lookaside 节点的 Depth 允许的情况下,释放的内存块被挂接在节点的 SLIST_ENTRY 链上。

下面的代码将分配 4 个内存块,然后分别释放它们:

NPAGED_LOOKAISDE_LIST Lookaside;
ExInitializeNPagedLookasideList(Lookaside, ...);// 初始化 Lookaside 节点
 
PVOID p1 = ExAllocateFromNPagedLookasideList(Lookaside);// 从节点里第 1 次分配内存
PVOID p2 = ExAllocateFromNPagedLookasideList(Lookaside);// 第 2 次分配内存
PVOID p3 = ExAllocateFromNPagedLookasideList(Lookaside);// 第 3 次分配内存
PVOID p4 = ExAllocateFromNPagedLookasideList(Lookaside);// 第 4 次分配内存
 
... ...
 
ExFreeToNPpagedLookasideList(Lookaside, p1);// 释放 p1
ExFreeToNPpagedLookasideList(Lookaside, p2);// 释放 p2
ExFreeToNPpagedLookasideList(Lookaside, p3);// 释放 p3
ExFreeToNPpagedLookasideList(Lookaside, p4);// 释放 p4 
 
... ...
 
//
// 下面代码再次进行分配内存
//
p1 = ExAllocateFromNPagedLookasideList(Lookaside);         // 此时从节点 ListHead 链里重复使用上次释放过的内存块!(也就是第1个释放的内存块)

在前面的分配 4 次内存里,都调用节点的 Allocate() 例程来进行物理的分配内存操作(因为节点的 ListHead 链是空的),将产生 4 个 AllocateMisses 事件!

在后续代码对分配得到的内存块进行释放,这里也遇到两种情形:

  1. 在 Depth 值允许的情况下:将内存块挂接在节点的 ListHead 链里,以备后续代码重复分配使用,它不会进行物理的释放工作。
  2. 在节点的 Depth 超限的情况下:将调用节点的 Free() 例程进行释放内存块工作,此时会产生 FreeMisses 事件,指示释放到 lookaside 链中失败!

前面的三次释放(p1,p2 以及 p3)会成功地挂接在节点的 ListHead 链表头里,而最后一次释放 p4 指向的内存块会导致进行物理地释放操作,回收到系统内存 pool 里(会记录在节点的 FreeMiesses 域里)。

Depth 超限
      当节点的 ListHead 域的 Depth 大于或等于节点的 Depth 值时,就属于 Depth 超限!
      也就是 Lookaside->L.ListHead.Depth >= Lookaside->L.Depth 时就超限了。

Lookaside->L.ListHead.Depth 代表着节点 SLIST_HEADER(也就是内存块链)的 Depth,而 Lookaside->L.Depth 代表示节点的 Depth,从前面的初始化例程里,我们知道节点的 Depth 最初的值为 4。因此:在初始化状态下节点的内存块链最多只能挂接 3 个内存块!超这个数量都将被释放掉,回收到系统 pool 池里。

在运行一段时间后(经过 N 次的分配与释放操作后),系统会定期检查分配的频率以及 miss 的频率,通过这些检查来动态调整节点的 Depth 值,提升节点的 Depth 额度来容纳更多的内存块。由此来达到更好的分配性能。

下面我们来看看 ExFreeToNPagedLookasideList() 例程的实现,在 windows 2003 里逆向出来下面的代码:

VOID ExFreeToNPagedLookasideList(
PNPAGED_LOOKASIDE_LIST Lookaside,
PVOID Entry
)
{
//
// TotalFrees 记录释放的次数,包括:物理释放和回收到 lookaside list 中
//
Lookaside->L.TotalFrees++;

//
// 释放的工作,主要有两个情形:
// 1.  当已经被分配的内存块超过 Lookaside list 所支持的深度时,就执行物理的释放工作(调用  Free() )
 // 2.  当小于 lookaside list 深度时,回收到 lookaside list 里,等待下次分配,不执行释放!(挂收在 list 里)
//
 
if (Lookaside->L.ListHead.Depth < Lookaside->L.Depth)
{
//
// 回收到 lookaside list 中
//
RtlpInterlockedPushEntrySList(Lookaside, (PSLIST_ENTRY)Entry);
}
else
{
//
// 记录回收到 lookaside list 失败次数,也就是:物理释放内存次数
//
Lookaside->L.FreeMisses++;
 
//
// 调用 lookaside list  节点中的 Free() 例程释放内存
//
Lookaside->L.Free(Entry);
}
}

我们清楚地看到两种情形:在回收到 lookaside list 时,代码调用 RtlpInterlockedPushEntrySList() 例程挂接在节点的 ListHead 链头里。在回到收系统内存 pool 时,就调用 Free() 来释放内存。

RtlpInterlockedPushEntrySList() 例程和 ExInterlocakPushEntrySList() 例程执行的操作是一样的,目的是:挂接在单向链表里。 在回收到 lookaside list 节点的 ListHead 链后,下次使用这个节点进行分配内存时,直接从 ListHead 链里取出内存块就行了(使用 ExInterlockedPopEntrySList() 例程)。

内存块被释放后,内存块里的数据变为“脏”数据(无用的),ExInterlocakPushEntrySList() 例程将内存块的第 1 个 4 字节数据作为 SINGLE_LIST_ENTRY 结构来挂接在 ListHead 链表头上。

在接着的分配内存中,ExInterlockedPopEntrySList() 例程取出 ListHead 链头的下一个节点,因此:下次分配时是取出第1个释放的内存块(也就是 p1 内存块)。那么:内存块首 4 个字节的 SINGLE_LIST_ENTRY 结构数据会被作为无用数据,在正常使用内存块会被覆盖!这一点巧妙地解决了 PVOID 指针与 PSLIST_ENTRY 指针的兼容性!

7. 动态调整 lookaside list 节点深度

kernel 会定期地监视 lookaside list 的使用情况,根据分配的次数频率来动态调整节点的深度。在初始化节点时 Depth 被置为 4(只能挂接 3 个内存块)。

kernel 会考虑下面情况:

  1. 当节点被频繁地分配内存,这 3 个内存块是不够的!在不足够的情况下会调用节点的 Allocate() 例程进行物理地内存分配操作。kernel 发现这种情况后会提升节点的 Depth 值,使节点能容纳更多的内存块。 如果这种情况持续发生,结果会提升到 256 值(最大的 Depth 值)。
  2. 在提升节点的 Depth 后,发现后续的分配次数和频率被降下来时。kernel 也会降低节点的 Depth 值。如果这种情况持续发生,最后会降至 4 值(最小的 Depth 值)。

通过这些调整手段来达到最好地利用 lookaside list 目的。kernel 使用 ExAdjustLookasideDepth() 例程来监视来调整 lookaside list,它的实现是:(从 windows 2003 逆出来)

VOID ExAdjustLookasideDepth()
{
//
// 分别扫描 3 个 lookaside list。
// 每次扫描的 lookaside list 都是不同的
//
switch (ExpScanCount)
{
case 0:
ExpScanGeneralLookasideList(ExNPagedLookasideListHead, ExNPagedLookasideLock);
break;
case 1:
ExpScanGeneralLookasideList(ExPagedLookasideListHead, ExPagedLookasideLock);
break;
case 2:
ExpScanSystemLookasideList();
break;
}
 
//
// 只有 3 个 lookaside list 需要扫描
//
ExpScanCount++;
if (ExpScanCount == 3)
{
ExpScanCount = 0;
}
}

这个例程会扫描 non-paged 链,paged 链以及 system 链,每次扫描的对象是不同的。对于 non-paged 链和 paged 链使用 ExpScanGeneralLookasideList() 例程来监视和调整

下面我们看看它的实现(从 windows 2003 里逆出来):

//
// 这个 routine 用来根据分配的频率动态调整 lookaside list 的深度。
// 
VOID ExpScanGeneralLookasideList(
PLIST_ENTRY ListHead,
PKSPIN_LOOK Look
)
{
KIRQL OldIrql = KeRaiseIrqlToDpcLevel();
 
PLIST_ENTRY ListEntry;
PNPAGED_LOOKASIDE_LIST Lookaside;
 
 
//
// 从尾部开始遍历整个挂在 ExNPagedLookasideListHead 链表头的 所有 PNPAGED_LOOKASIDE_LIST 节点!
//
// 这个链由 ExNPagedLookasideListHead 作为链表头(LIST_ENTRY 结构)
// 它的节点是 PNPAGED_LOOKASIDE_LIST 结构的内的  ListEntry 成员!
//
 
for (ListEntry = ListHead->Flink; // 由尾部点节开始
ListEntry != ListHead; // 直到 head 部
ListEntry = ListEntry->Flink// 迭代
)
{
 
//
// 得到宿主的 NPAGED_LOOKASIDE_LIST 结构的地址
// 结果等于: Lookaside = (NPAGED_LOOKASIDE_LIST *)((ULONG)ListEntry - 0x30)
//
Lookaside = CONTAINING_RECORD(ListEntry, NPAGED_LOOKASIDE_LIST, ListEntry);
 
 
//
// 计算扫描时的分配情况
//
ULONG AllocateMisses = Lookaside->L.AllocateMisses - Lookaside->L.LastAllocateMisses;
ULONG TotalAllocates = Lookaside->L.TotalAllocates - Lookaside->L.LastTotalAllocates;

Lookaside->L.LastAllocateHits = Lookaside->L.AllocateHits;
Lookaside->L.LastTotalAllocates = Lookaside->L.TotalAllocates;

USHORT Depth = Lookaside->L.Depth;
USHORT MaximumDepth = Lookaside->L.MaximumDepth;
 
if (ExMinimumLookasideDepth != 0)
{
 
//
// 测试是否频繁使用 lookaside 进行分配
// 当分配次数(包括成功和失败) 达到 75 次就视为频繁
//
if (TotalAllocates >= 75)
{
ULONG MissPercent = AllocateMisses * 1000 / TotalAllocates;
 
//
// 假如 miss 次数低于  50% 则降低 depth ,最小的 depth 为 4
//
if (MissPercent < 5)
{
Depth--;// 逐步降低节点 Depth 值
if (Depth < 4)
Depth = 4;
}
else
{
//
// 计算提高 depth 的幅度,
// 根据剩余的额度来调整:最多每次提升 30 ,最大的 depth 为 256
//
LONG RaiseDepth = ((MaximumDepth - Depth) * MissPercent) / 2000;
if (RaiseDepth > 30)
{
RaiseDepth = 30;
}

Depth = Depth + RaiseDepth;// 提升节点  Depth 值
 
if (Depth > MaximumDepth)
{
Depth = MaximumDepth;
}
}
}
else
{
// 
// 降低 lookaside list 节点的 depth
// 当使用不频繁时,下降的幅度为 10
//
Depth = Depth - 10;
if (Depth < 4)
Depth = 4;
}

 
Lookaside->L.Depth = Depth;// 调整节点 Depth 
}

kfLowerIrql(OldIrql);
}

Depth 调整的范围始终在 4 到 256 之间,这是 kernel 定义的最小值与最大值。

至此,lookaside list 的实现基本分析完毕!

时间: 2024-10-06 09:28:56

[转载]使用 Lookaside List 分配内存的相关文章

[转载]C语言程序的内存分配方式

"声明一个数组时,编译器将根据声明所指定的元素数量为数量为数组保留内存空间."其实就是编译器在编译的过程中,会加入几条汇编指令在程序里处理内存分配,并不是说编译时就分配了内存,不要理解错了. ------------------- 1.内存分配方式 内存分配方式有三种: [1]从静态存 储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. [2]在栈上创建. 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结

分配内存(Linux设备驱动程序)

分配内存 介绍设备驱动程序中使用内存的方法: 如何最好地利用系统内存资源. kmalloc函数 kmalloc内存分配引擎是一个功能强大的工具. #include <linux/slab.h> void *kmalloc(size_t size, int flags); 参数分配表示flags能够以多种方式控制kmalloc的行为. 标志GFP_KERNEL表示内存分配是代表运行在内核空间的进程执行的,这意味着调用它的函数正代表 某个进程执行系统调用. 使用GFP_KERNEL允许kmallo

转载 Memcached BinaryProtocol incr指令内存泄露的bug

缘起 最近有个分布式限速的需求.支付宝的接口双11只允许每秒调用10次. 单机的限速,自然是用google guava的RateLimiter. http://docs.guava-libraries.googlecode.com/git-history/master/javadoc/com/google/common/util/concurrent/RateLimiter.html 分布式的ReteLimiter,貌似没有现在的实现方案.不过用memcached或者Redis来实现一个简单的也

ubuntu中报错:无法分配内存 (errno=12)

今天碰到一个大坑,差点要了老命! 之前装了ubuntu双系统,后来崩溃,想在就想装VMware虚拟机,再装ubuntu,一切进展顺利,直到在虚拟机的ubuntu中安装IDEA时出现了问题. 安装过程并没有报错,但是启动后运行一个项目,项目还没加载呢就直接退出,生成个错误日志,如下: OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x00000000f5a9b000, 66166784, 0) failed; error='无法

OpenCV 2.4.9 学习笔记(3)—— OpenCV自动为输出数组(矩阵)分配内存

OpenCV大部分时候会为OpenCV方法中的输出数据(方法的参数)自动分配内存,所以如果一个方法的参数有一个或者多个输入数组(cv::Mat 实例)和一些输出数组时,OpenCV会自动为输出数组分配内存空间或者重新分配内存空间,内存的大小和数据类型由输入数组决定.如果需要的话,这个方法会通过其他的参数来决定输出数组的其他属性. 下面用一段代码来说明一下: 1 #include "cv.h" 2 #include "highgui.h" 3 4 using name

php内核为变量的值分配内存的几个宏

在php5.3之前,为某变量分配内存是用宏 MAKE_STD_ZVAL; 737 #define MAKE_STD_ZVAL(zv) \ # /Zend/zend.h738 ALLOC_ZVAL(zv); \739 INIT_PZVAL(zv); 165 #define ALLOC_ZVAL(z) \ # /Zend/zend_alloc.h166 (z) = (zval *) emalloc(sizeof(zval)) 727 #define INIT_PZVAL(z) \          

在dll里malloc/new/cvCreate分配内存,在exe里free/Releases释放内存时会出错。

写了个程序,在DLL中用malloc分配了一块内存,但是在exe程序中释放,结果程序crash,原因就是:其原因可能是堆被损坏,这也说明 TestMySticker.exe 中或它所加载的任何 DLL 中有 bug. 以下文字引用自 http://hi.baidu.com/huhe/blog/item/0b422edd1f1563d98c1029a3.html 一个模块一个堆,一个线程一个栈. dll里malloc的内存,在exe里free会出错. CRT(C运行时期库)不是使用进程缺省的堆来实

C++ 内存、new与malloc分配内存区别?

一关于内存 1.内存分配方式 内存分配方式有三种: (1)从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. (2)在栈上创建.在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放.栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限. (3) 从堆上分配,亦称动态内存分配.程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或de

[转载]对iOS开发中内存管理的一点总结与理解

对iOS开发中内存管理的一点总结与理解 做iOS开发也已经有两年的时间,觉得有必要沉下心去整理一些东西了,特别是一些基础的东西,虽然现在有ARC这种东西,但是我一直也没有去用过,个人觉得对内存操作的理解是衡量一个程序员成熟与否的一个标准.好了,闲话不说,下面进入正题. 众所周知,ObjectiveC的内存管理引用的一种叫做“引用计数“ (Reference Count)的操作方式,简单的理解就是系统为每一个创建出来的对象,(这里要注意,只是对象,NSObject的子类,基本类型没有‘引用计数’)