_heap_init
在(1)中提到过该函数用于分配一个堆。这个堆是动态创建的私有堆,与系统为进程分配的默认堆不同。进程启动时,系统会在进程虚拟地址空间中创建一个堆,即为进程的默认堆。默认堆的创建和回收均由系统来完成。除了默认堆,进程中还可以存在若干个私有堆。私有堆可以由进程动态创建,并且在此基础上进行内存分配、释放等操作。
_heap_init函数本质上是调用了HeapCreate函数,HeapCreate也是kernel32.dll提供的API。在CRT0.c 中对_heap_init 的调用为:
#ifdef _MT
if ( !_heap_init(1) ) /* initialize heap */
#else /* _MT */
if ( !_heap_init(0) ) /* initialize heap */
#endif /* _MT */
fast_error_exit(_RT_HEAPINIT); /* write message and die */
这里的_MT表示是否使用运行时库(CRT)的多线程静态版本:(测试环境VC6)
- 如果在VS中创建一个MFC APP,会加上 _MD
编译选项,表示 _MT
和 _DLL
均被定义,app使用运行时库的多线程版本
- 如果在VS中创建一个Console APP,会加上_ML
编译选项,app使用单线程静态版的
运行时
库
这里以普通的CUI程序为例,不会定义_MT
,所以采用的堆的初始化方式是_heap_init(0)。来看一下_heap_init 的源代码:
int __cdecl _heap_init (int mtflag)
{
// Initialize the "big-block" heap first.
if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )
return 0;
// Pick a heap, any heap
__active_heap = __heap_select();
if ( __active_heap == __V6_HEAP )
{
// Initialize the small-block heap
if (__sbh_heap_init(MAX_ALLOC_DATA_SIZE) == 0)
{
HeapDestroy(_crtheap);
return 0;
}
}
else if ( __active_heap == __V5_HEAP )
{
if ( __old_sbh_new_region() == NULL )
{
HeapDestroy( _crtheap );
return 0;
}
}
return 1;
}
在本例中,相当于执行了:
int __cdecl _heap_init (int mtflag)
{
// Initialize the "big-block" heap first.
if ( (_crtheap = HeapCreate( HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL )
return 0;
// Pick a heap, any heap
__active_heap = __heap_select();
return 1;
}
代码中第四行的_crtheap 是定义在 heapinit.c 开头处的一个全局变量。它表示当 HeapCreate 运行成功之后,返回一个指向新分配堆的句柄。HeapCreate 函数原型如下:
HANDLE WINAPI HeapCreate(
__in DWORD flOptions,
__in SIZE_T dwInitialSize,
__in SIZE_T dwMaximumSize
);
第一个参数flOptions 是 HEAP_NO_SERIALIZE。HEAP_NO_SERIALIZE 表示可以对堆采用非序列化的访问。采用序列化的访问可以避免在多个线程在同一块堆上分配内存时产生冲突,这有点儿加上一把同步锁的意思。但如果指定了HEAP_NO_SERIALIZE ,则这种串行访问的方式就被去除掉了。也就是说,不同的线程可以同时用 Handle 去操作同一个堆,这显然容易导致堆内存的破坏。
摘自msdn
HEAP_NO_SERIALIZE
可以用于下列情形:
1.进程仅拥有一个线程
2.进程拥有多线程,但仅有一个线程堆指定的堆调用堆函数
3.进程拥有多线程,且应用程序提供他自己的机制来处理指定堆的互斥
第二个参数dwInitialSize 指明了堆的初始大小,并且这个值总是要对其到内存中一张page页面的大小。这与PE文件映射进内存中,不同的section之间需要进行对齐是一个道理。通常,win32平台下一张page的大小是0x1000 bytes。
WINHEAP.H头文件中定义:
#define BYTES_PER_PARA 16
#define PARAS_PER_PAGE 256 // tunable value
#define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE)
这样计算出来 16 * 256 = 16^3 = 0x1000 bytes
第三个参数dwMaximumSize指明了堆的最大字节数。dwMaximumSize = 0 时,被创建的堆可以在需要的时候被扩建,是一个可增长堆,理论上这个堆的最大大小仅仅受限于可用的内存大小。当dwMaximumSize !=0 时,那么堆的大小是固定的,并且不能大于dwMaximumSize指定的范围。
更详细的可以参考:http://msdn.microsoft.com/en-us/library/aa366599%28VS.85%29.aspx
当HeapCreat 执行完毕后,会接着调用__heap_select 函数。__heap_select 负责为之前创建的堆指定类型,并且存放在全局变量 __active_heap 中,__active_heap 会影响到后续 malloc 时的内存分配方式。
WINHEAP.H头文件中定义了三种堆:
// Heap-selection constants
#define __SYSTEM_HEAP 1
#define __V5_HEAP 2
#define __V6_HEAP 3
对于WIN2K向上的所有系统,都会将堆指定为 __SYSTEM_HEAP 类型,因为在__heap_select 函数中有如下实现:
OSVERSIONINFO osvi;
// First, check the OS for NT >= 5.0
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if ( GetVersionEx(&osvi) )
if ( (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osvi.dwMajorVersion >= 5) )
return __SYSTEM_HEAP;
至于__V5_HEAP、__V6_HEAP暂时不做深究。
分享到:
相关推荐
最终,`__rt_entry`会调用用户定义的`main`函数,并在完成执行后退出。 #### 2. __main实现 **2.1 工程配置** 为了使`__main`函数能够正确地执行,需要对KEIL工程进行适当的配置。 - 当选择使用MicroLIB时,需要...
3.抓取步骤2进程的堆栈数据,如进程pid是4723 am dumpheap -n 4723 /data/00.txt am dumpheap -n 4723 /data/01.txt 4.格式化堆栈数据 python native_heapdump_viewer.py --symbols symbols 00.txt >00.log python ...
标题中的“test_heap_sort.rar_heap”表明这是一个关于堆排序(Heap Sort)的程序实现,使用了VC++(Visual C++)编程语言。堆排序是一种基于比较的排序算法,它的核心思想是利用二叉堆的数据结构来对数组进行排序。...
- **起始地址**:通过宏定义 `#define HEAP_BEGIN ((void*)&Image$$ARM_LIB_HEAP$$ZI$$Base)` 定位到了堆区的起始地址。 - **结束地址**:通过宏定义 `#define HEAP_END ((void*)&Image$$ARM_LIB_HEAP$$ZI$$Limit)` ...
"Java.lang.OutOfMemoryError: Java heap space 解决...Java.lang.OutOfMemoryError: Java heap space 是一种常见的 Java 错误,解决该问题需要从多方面入手,包括调整 JVM 的堆大小、调整 Tomcat 的配置、优化程序等。
2. 排序循环:不断调用`pop_heap`函数将堆顶元素(当前最大元素)与末尾元素交换,然后调整堆,使得剩余元素仍然是一个堆。 3. 结束:当堆大小减至1时,排序完成。 堆数据结构在许多实际应用中非常有用,例如优先...
最近在学习STL的源代码,看到这么多优秀的代码,心里痒痒的,于是自己实现了一遍,当然,有自己的特色,都是模块函数,稍稍用了一些traits特性。相互学习,呵呵
### Java 错误处理:java.lang.OutOfMemoryError: Java heap space 在Java应用程序开发过程中,经常遇到的一个问题就是内存溢出错误,特别是在处理大量数据或长时间运行的应用时。其中,“java.lang....
解决 Java_heap_space 问题 Java_heap_space 问题是 Java 应用程序中一个常见的问题,它是指 Java 虚拟机(JVM)堆空间不足,导致应用程序无法正常运行的错误。在本文中,我们将详细解释 Java_heap_space 问题的...
Ion_chunk_heap.c文件很可能是实现这些功能的源代码,包括内存块的管理逻辑、分配和释放的函数、内存保护策略等。通过分析这个源代码,我们可以深入了解Ion内存管理的内部细节,以及它是如何与Android系统的其他组件...
在`ion_heap.c`源文件中,我们可以看到ION V2 heap的具体实现细节,包括堆的初始化、内存分配和释放的函数,以及相关的数据结构定义。源代码通常包含了错误检查、边界处理、内存对齐计算等核心逻辑,是理解ION V2 ...
### Java.lang.OutOfMemoryError:Java Heap Space 错误及处理办法 在Java应用程序开发与维护过程中,经常会遇到`java.lang.OutOfMemoryError: Java heap space`这一异常情况。该异常通常表明Java虚拟机(JVM)的堆...
3. `std::pop_heap`: 这个函数从堆的顶部(通常是最大元素的位置)移除元素,并保持堆的性质。移除的元素被移到序列的末尾,所以通常与`back()`或`pop_back()`配合使用来删除元素。 4. `std::sort_heap`: 它将堆...
- `IMPORT __main`:声明外部函数`__main`,这是一个由运行时库提供的函数,用于初始化堆栈、堆,并最终跳转到用户程序的入口点。 - `LDR R0, =__main`:加载`__main`函数的地址到寄存器`R0`。 - `BX R0`:跳转到...
`push_heap`函数通常会将新元素添加到堆的末尾,并通过一系列交换操作(比如下沉操作)来确保堆的性质得到维护。而`.pass.c`可能表示这是一个经过编译器优化后的版本,可能在性能或效率上有所提升。 在`quarantine....