CMU 15-213 Introduction to Computer Systems学习笔记(18) Dynamic Memory Allocation: Basic Concepts

之前学习的虚拟内存机制,以及它如何为系统提供如此多的有功功能,随着这种大型连续字节数组的抽象,现在一旦我们获得了大量的字节,我们必须管理它,我们必须有一些机制来管理和使用这些资源,本周的主题是存储器的分配,以及储存器分配器的工作原理,以及如何使用它们来管理内存中的虚拟系统。

Basic concepts

Dynamic Memory Allocation 动态内存分配

动态储存器分配器的基本思想是:应用程序使用它去操纵虚拟内存,去构造、分配以及释放虚拟存储器片,在你的程序中会需要这些虚拟存储器,而且这个存储器在称为堆的虚拟存储器区域汇总被维护,所有语言都有一些用来分配和操纵动态存储器的机制,在C中,使用的是malloc的方式。但是像Java这样的语言使用的是其他新的方式,

Dynamic Memory Allocation

分配器将堆维护为连续的块的集合,块可以被分配或者被释放,被分配意味着它们被某些应用程序使用,空闲意味着可供应用程序使用,有两种类型的分配器。你可以在C中找到的那种分配器,比如malloc包,这种分配器,应用程序将决定是否显示分配内存,并在应用程序完成后是否显示的释放内存,所以系统不会释放你分配的任何内存,除非你通过调用free函数显示的释放内存。但是还有其他语言支持隐式分配器,在这些隐式分配器中,程序员显示地分配内存,释放的内存的负担从应用程序转移到系统,并且它使用称为垃圾收集的过程隐式的释放了这些内存。所以像Java,ML,Lisp这样的语言都支持隐式分配器(implicit allocatros)

这节课主要讲的是显示分配器。

The malloc Package 

C语言中的分配器由C标准库中一组交malloc包的函数提供,它把以字节作为单元的参数作为函数的输入参数,然后返回一个指向内存块的指针,该内存块至少包含所声明的大小的字节,并且该块在x86系统上以8字节对齐,在x86-64系统上以16字节对齐。

程序员通过调用free函数释放内存,它以先前调用malloc时返回的指针作为参数。

使用sbrk函数可以调整heap的大小。

malloc Example 分配例子

上面的代码示例中,我们有一个指向整形的指针p,我们调用malloc,我们想要分配一个大小为n的整形数组,malloc返回一个泛型指针 void*,我们将它转换为指向整形的指针,以便于编译器编译,然后将其分配给p,我们检查返回值是否为空,应该指出它在出错时返回0,所以我们会重新检查这个指针是否是空指针,如果他为空,则打印错误,一旦我们得到了指针,我们就可以像对待一个数组一样对待他,完成分配这块内存后,我们通过只用指针p调用free来释放。

Assumptions Made in This Lecture 这节课做的假设

这节课要看看如何实现malloc和free等功能,这个课要做一些假设,我们知道内存的地址是按照字节的地址,我们只是关注以字为大小的单位,这里以字来说明。我们假设word字是4个字节,他们基本上是一个int的大小,这些字所构成的块能被分配或者释放。

Allocation Example 分配例子

Constraints限制

唯一的一个事实是,当它释放某些东西的时候,必须使用来自之前调用malloc时所返回的指针,malloc运行或者像malloc这样的分配器在很多不同的约束条件下运行,它们无法控制分配块的大小或者数量,因为他们无法控制应用程序正在做的事情,如果应用程序调用malloc malloc必须立即响应,当然必须从可用内存中分配块,一般来说不能触及任何已经分配的块,一旦它分配了那个块 那么该块就属于那个应用程序,而malloc包不能触及它,这意味着分配器不能移动块,比如分配器无法将已经分配的块放到一起以达到压缩块的目的,

Performance Goal: Throughput 吞吐量

分配器有趣的原因还有:它包括了时间和空间上的权衡,这是一种空间和性能的权衡,你正尝试使其运行得尽可能快,但是你希望尽可能高效地使用堆中的虚拟内存,我们定义了这些速度和内存效率指标。我们使用了两个指标,一个称为吞吐量

吞吐量评价malloc处理来自应用程序的这些请求的效率。

Performance Goal: Peak Memory Utilization峰值内存利用率

峰值内存利用率是一种衡量多少有用空间的标准,它衡量分配器使用堆的效率有多大,去衡量分配器在实现中必须使用的数据结构中的各种开销上浪费了多少,我们将定义一个payload,一个应用程序调用malloc以请求一定大小的块,该块称为payload有效载荷。因此如果我们使用10个字节的参数调用了malloc,我们要求一个有效载荷至少为10大小的块,我们请求的10个字节称为有效负载,该块中的其他内容都是overhead开销。

在完美的分配器中,聚合有效负载等于内存量,等于所有已分配块的总大小,因为没有开销,每个块都是有效载荷。

如果我们假设heap是单调非递减的,所有它总是在变大,这是简单的假设,真正的malloc包不是这样,

Uk就是我们的利用率,当利用率为1时,代表完美的分配。单实际上,在分配器将要放置的每个块的内部,都有数据结构和其他内容,这样会没法得到完美的利用率,一个显而易见的事是,因为块必须对齐,如果它们是16字节对其的,那么块必须以16字节边界开始,因此如果你请求2字节的有效负载,那么你有很多浪费的字节。因此有一些开销是不可避免的,要做的就是尽量减小损失。

Fragmentation碎片

我们刚才谈到的例子就是我们称之为fragmentation【碎片】的例子,而且有两种类型的碎片,

Internal Fragmentation

内部随便是刚才谈论到的,可能是由块中的填充或分配器需要的块中的某种数据结构引起的。

External Fragmentation

Knowing How Much to Free

Keeping Track of Free Blocks

如何记录free block ?

方法一:隐式列表

方法二:显示列表 利用块中的一些字,去创建某种链接列表,单链或者双链表,

方法三:单独的free list    list拥有不同的size

方法四:通过平衡树分割

Implicit free lists

Method 1: Implicit List

Detailed Implicit Free List Example

记住块的有效负载总是必须从8字节边界开始,是有效负载,而不是前面保存size的区域。

Implicit List: Finding a Free Block

最佳适配(best fit)法看起来会带来更大的消耗,因为你在找到合适的东西之前,你必须一直寻找,你必须扫描所有的空闲块,然后选择最好的区域,但是它有一个很好的属性,它提高了内存利用率,这是一个典型时空权衡的例子,它的速度较慢,但是提供了我们使用内存的效率。

Implicit List: Allocating in Free Block

Implicit List: Freeing a Block

Implicit List: Bidirectional Coalescing

Constant Time Coalescing

如果是case 1 我们只需要将分配状态设置为空闲。

如果是case2,我们要做的是检查前一个块的边界标记,我们看到它被分配,所以没什么可以做的,我们使用n跳转到下一个块的头部,我们看到它的下一个状态是空闲的,所以这两个块需要被合并,我们只需要将两个大小相加就可以创建更大的合并块,并将其分配状态设置为零。

如果是case3,和case2差不多

当我们检查前后块都是空的时候,我们就创造一个单个块,和为三个块。

Disadvantages of Boundary Tags

No Boundary Tag for Allocated Blocks (1)

No Boundary Tag for Allocated Blocks (2)

Summary of Key Allocator Policies

Implicit Lists: Summary

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值