尝试动态内存分配c ++

时间:2018-04-06 08:09:21

标签: c++ winapi

我试图通过使用指针来分配1 KiB的内存

GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
        std::cout << pmc.WorkingSetSize << " Current physical memory used by the process" << std::endl;
        int a = pmc.WorkingSetSize;
        char *test= new char[1024];

GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
        int b = pmc.WorkingSetSize;
            std::cout << "Actual allocated  " << (b - a) / 1024 << std::endl;

问题是每次我运行这段代码它似乎分配在100 KiB到400 KiB之间的任何地方我使用了char,因为它的大小是1字节

3 个答案:

答案 0 :(得分:8)

为了能够更快地满足未来(预期)分配(无需涉及操作系统),实施可以免费分配超出您背后的要求。这通常是一个很好的优化,没有什么可担心的 - 特别是因为操作系统甚至可能在您实际写入它们之前甚至没有用实际物理页面来支持那个allcation,因此它通常不需要任何费用。

无论如何,从C ++语言的角度来看;只要你得到你所要求的记忆,无论你实际分配的内容是否通常不是你可以知道的东西,也不是你应该关心的东西。

答案 1 :(得分:4)

new背后的代码知道操作系统。它通常知道哪些分配大小是有效的。事实证明,大多数程序都会进行更多更小尺寸的分配。因此,new后面的代码通常会要求更大的内存块,并根据需要细分每个块。

看起来Private Sub Button1_TxtChng(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TextBox1.KeyDown, TextBox2.KeyDown Dim num1, num2 As String num1 = TextBox1.Text num2 = TextBox2.Text If IsNumeric(TextBox1.Text) Then 'The compare code should be a function' If num1 = num2 Then Me.Label1.BackColor = Color.Orange ElseIf num1 < num2 Then Me.Label1.BackColor = Color.Red Else Me.Label1.BackColor = Color.LightGreen End If Else TextBox1.Clear() End If If IsNumeric(TextBox2.Text) Then 'The compare code should be a function' If num1 = num2 Then Me.Label1.BackColor = Color.Orange ElseIf num1 < num2 Then Me.Label1.BackColor = Color.Red Else Me.Label1.BackColor = Color.LightGreen End If Else TextBox2.Clear() End If End Sub 在您的情况下要求大约100kB,分发1KB,并且具有99kB以满足未来的请求而不会打扰操作系统。

答案 2 :(得分:2)

::operator new正在分配内存(如文档所述),由C++ standard library提供(这是一些用英语编写的规范和部分C ++标准,例如起草的在n3337)。在您的系统和计算机上,它的实现是使用operating system服务(原则上,您可能有一些C ++标准库实现,它不使用任何操作系统并在某些裸硬件上运行,但我不知道)。你应该期待new&amp; delete需要一些内部开销并消耗资源(但您不应该真正关心并希望它保持合理)。

阅读Operating Systems: Three Easy Pieces (可自由下载的操作系统教科书)。

这个术语并非完全是双方同意的,因为微软有own。但是,该书中的概念和解释适用于大多数当前的操作系统,包括Microsoft Windows。您还可以找到各种讲座和幻灯片,解释类似的概念,术语略有不同。但我发现textbook编写得很好,我建议阅读它。一旦你理解了这本书的大部分内容,你就可以自己回答你的问题,并发现术语上的差异。

您的C ++标准库实现可能正在从您的heap memory请求operating system(可能使用某些特定于操作系统的system calls - Linux,其中包括mmap(2),我没有&#39;知道Windows上可用的系统调用;该信息可能not是公开的。当然,virtual address spaceprocess增长了pages的倍数。

维基百科的虚拟地址空间defined

  

&#34; 操作系统提供给进程的虚拟地址范围集&#34;

和(如果使用该定义)它确实发生变化(可能VirtualAlloc - 可能由newmalloc - &amp; LoadLibrary和其他函数调用。顺便说一句,在x86-64上,页面大小(由硬件决定,例如MMU)通常是4k字节,因此你的虚拟地址空间不可能只增长1024字节。

(请参阅以下PS中的说明;对于IinspectableMicrosoft,&#34;虚拟地址空间&#34;具有不同的定义,因此意味着不同的东西;我在维基百科的定义中使用它,然后它可能随时间而变化)

两者之间没有保证关系(new分配的内存和OS提供的虚拟地址空间)。在许多系统中,new调用malloc(来自C标准库)从操作系统请求内存但更喜欢在可能的情况下重用以前的free - d内存区域。某些C ++标准库实现可能直接调用OS服务进行内存管理。细节是特定于实现的。

向操作系统内核询问堆内存并将其释放回来通常是一项昂贵的操作。因此,您的实现通常更喜欢要求比所需更多的内存,并保留以前delete - d(或free - d)内存区域以供以后重用。

BTW,如果你使用Linux,所有的C ++标准库,C ++编译器,C标准库和内核都是free software,你可以研究它们的源代码来获得所有的血淋淋的细节。

请注意,虚拟地址空间确实已更改(通过mmap(2)以及Linux上的相关系统调用,VirtualAlloc以及其LoadLibrary等其他功能,以及可能是Windows上的其他几个系统功能)。顺便说一句,在Linux上,您可以使用proc(5)(带/proc/self/maps)以编程方式查询该虚拟地址空间。我想在Windows上有一些方法可以查询它。当操作系统为进程提供的一些&#34;虚拟地址范围时,VAS正在发生变化。改变,例如当一个地区发生变化时(如在Windows上成功VirtualAlloc或在Linux上成功mmap)。

Iinspectable在他的评论中声称虚拟地址空间永远不会改变,但他以不同且不寻常的方式定义了虚拟地址空间。我坚持使用维基百科的定义,这种定义更为常见,甚至在20世纪70年代用于IBM大型机

  

它似乎分配在100 KiB到400 KiB之间的任何地方

您可能需要研究C ++标准库实现的源代码(可能很难或很难获得)来解释它。我的猜测是,这种变化可能与ASLR有关(但这只是一个盲目的猜测,我可能是错的)。

如果您担心实际内存消耗,则可以重新定义自己的::operator new实现(以及相关的,delete)。以下内容可能符合标准的字母:

void* ::operator new  ( std::size_t count ) { throw std::bad_alloc; }

但实际上是没用的。另请参阅malloc的{​​{3}} {我}。随意改进!

(两者都是笑话;只是为了表明在实践中你期望的不仅仅是标准的字母)

PS。似乎Microsoft proposed implementation(以及来自documentation的评论)使用&#34;虚拟地址空间&#34;与维基百科的定义不同。我正在关注维基百科Iinspectable,这也是definition中使用的维基百科(见Operating Systems: Three Easy Pieces)。当然,虚拟地址空间的可能范围由chapter 13和处理器(ISA上的IIRC 48位)定义,请参阅Ryzen wikipage关于规范表单地址的figure )。有些人打电话给#34; x86-64&#34;操作系统为进程提供的那些[个人]&#34;虚拟地址范围中的每一个&#34;但这个术语并不普遍,分段记忆可能意味着不同的东西。其他人谈论同一事物的虚拟内存区域(进程的虚拟地址空间中的有效地址的单个连续间隔)。 AMD文档提到了segment中的虚拟内存空间(256TB),用于实现虚拟地址空间(由操作系统)。其他一些文档将虚拟地址范围称为虚拟地址中的有效位数(它受硬件限制,例如§2.1.5.1 AMD Ryzen)。关键是操作系统管理每个进程的虚拟地址空间,并且可以分配&#34;区域&#34; - 或&#34;细分&#34; - 这是连续的地址范围。请记住,进程是由OS内核管理的工件(它们不存在于硬件中;进程是OS提供的抽象)及其调度程序。虚拟地址空间也是由OS管理的工件(某些进程的一部分或属性)。在Linux上,MMU被定义为&#34;在虚拟地址空间中创建新映射&#34;那么正在改变那个VAS。

PPS mmap(2)working set以及resident set sizethrashing是相关概念(但不同于虚拟地址空间)。虚拟地址空间是每个进程的基本但不变的属性(每个进程都有自己的进程)。 operator newmalloc 的实施可能会更改您的流程的VAS(但不会每次更改)(例如,通过调用VirtualAlloc在Windows上,或Linux上的mmapsbrk。如果你坚持使用微软的virtual memory,那么虚拟地址空间意味着不同的东西永远不可变(但这不是很有趣,并且不是过程的一个特征,而是硬件的一个特征,AMD称之为&#34;虚拟内存空间&#34 ;;另见definition)。

NB。 我不了解Windows并且从未使用它(并希望在我的职业生涯中永远不会对它进行编码)。但是我从1974年开始编程并且自1987年以来一直使用类Unix系统。今天我是Linux用户,我知道关于GCC编译器内部的this