当前位置: 首页 > news >正文

内存分配.

内存分配

  • 一、在堆上分配内存
  • 1.调整program break:brk和sbrk
    • 2.在堆上分配内存:malloc()和free
      • 3.调用free还是不调用
        • 4.malloc和free的实现
        • 5.在堆上分配内存其他方法
        • 6.分配对齐的内存
    • 二、在堆栈上分配内存

一、在堆上分配内存

进程可以通过增加堆的大小来分配内存,所谓堆是一段长度可变的连续虚拟内存,始于进程的未初始化数据段末尾,随着内存的分配和释放而增减。将堆的当前内存边界称为"program break"

1.调整program break:brk和sbrk

program break的位置抬升后,程序可以访问新内存区域内的任何内存地址,而此时物理内存页尚未分配。内核会在进程首次试图访问这些虚拟内存地址时自动分配新的物理内存页。

#include<unistd.h>
int brk(void *end_data_segment);//将program break设置指定位置
//设置低于初始化值结果难以预料。

void *sbrk(intptr_t increment);//在原有地址上增加从increment传入的大小。

2.在堆上分配内存:malloc()和free

malloc优点:

  1. 更易于在多线程程序中使用;
  2. 接口简单,允许分配小块内存;
  3. 允许随意释放内存块,被维护于一张空闲内存表中,后续内存分配调用循环使用。
//在堆上分配大小为size的内存,并返回指向新分配内存起始位置处的指针,其所分配的内存未经初始化。
#include<stdlib.h>
void *malloc(size_t size);
  1. malloc函数返回类型为void*,所以可以分配任意类型的C指针。

  2. malloc函数返回内存所采用的字节对齐方。

  3. 无法分配内存返回NULL,设置errno返回错误信息。

//释放ptr所指向的内存块,由malloc返回的地址
#include<stdlib.h>
void free(void *ptr);
//传入是空指针,则什么都不做。

free并不降低program break的位置,而是将这块内存添加到空闲内存列表中,供后续的malloc函数循环使用,原因:

  1. 被释放的内存通常会位于堆的中间,而非堆的顶部。

  2. 最大限度地减少程序必须执行的sbrk调用次数。

  3. 在多数情况,降低program break的位置不会对那些分配大量内存的程序有多少帮助。因为通常倾向于持有已分配或是反复释放和重新分配内存,而非释放所有内存后持续一段时间。

3.调用free还是不调用

  • 进程终止时,其占用的所有内存都会返还给操作系统,包括堆内存中由malloc函数包含一系列函数所分配的内存。

    • 基于内存自动释放机制,对于那些分配了内存并在进程终止前持续使用的程序而言,通常会省略对free的调用。

      • 依靠指针进程自动释放内存可以接受,但基于以下原因,最好显示释放已分配的内存:
      1. 显示调用free函数能使程序在未来修改时更具有可读性和可维护性。
      2. 若使用malloc调试库来查找程序内存泄漏问题,则将任何未经显示释放处理的内存报告为内存泄漏,使工作复杂化。

4.malloc和free的实现

malloc实现:

  1. 首先扫描之前由free所释放的空闲内存块列表,寻求合适的空闲内存块。

  2. 内存块合适直接返回给调用者,更大内存则将其分割,在将一块合适内存返回给调用者,把小的留在空闲列表中。

  3. 若内存无足够大空闲内存块,那么malloc调用sbrk分配内存。

free实现:

  1. malloc分配内存块时,会额外分配字节几个来存放记录这块内存大小的整数值。该整数位于内存块的起始处,而实际返回给调用者的内存地址恰好位于这一长度记录字节之后。
    在这里插入图片描述

  2. 当将内存置于空闲内存列表时,free使用内存块本身的空间来存放链表指针,将自身添加到列表中在这里插入图片描述

  3. 随着对内存不断释放和重新分配空闲列表中的空闲内存会和已分配的在用内存混杂在一起

在这里插入图片描述

分配和释放时注意以下规则:

  1. 分配一块内存后,不要改变这块内存范围外的任何内容,错误的指针运算。

  2. 释放同一块已分配内存超过一次的错误。

  3. 若非经由malloc函数包中函数所指向的指针,绝对不能在调用free函数时使用。

  4. 反复分配内存,确保释放所有已使用完毕的内存。

5.在堆上分配内存其他方法

//分配内存
#include<stdio.h>
void *calloc(size_t numitems,size_t size);
//numitems:数量
//size:大小
//成功:返回指向这块内存起始处的指针,无法分配NULL
//将已分配的内存初始化为0
//free释放


struct {}myStruct;
struct myStruct *p;
p = calloc(1000,sizeof(struct myStruct));
if(p==NULL)
{...}
//调整一块内存大小,此块内存由malloc分配的
#include<stdlib.h>
void *realloc(void *ptr,size_t size);
//ptr:指向需要调整大小的内存块指针。
//size:大小期望值。
//成功:返回指向大小调整后内存块的指针,错误NULL。
//增加已分配内存块的大小,则不会对额外分配的字节进行初始化。
free释放

//重新定位由ptr指向的内存块
nptr = realloc(ptr,newsize);
if(nptr==NULL){}else{ptr=nptr;}

6.分配对齐的内存

分配内存起始位置要与2的整数次幂对齐。


#include<malloc.h>

void *memalign(size_t boundary,size_t size);
//分配size个字节内存
//起始位置参数boundary的整数倍
//返回已分配内存的地址。
//free释放

int posix_memalign(void **memptr,size_t alignment,size_t size);
//出错返回错误号
//free释放


int s;
void *memptr;
s = posix_memalign(&memptr,1024*sizeof(void *),65536);

memalign和 posix_memalign不同之处:

  1. 已分配的内存地址通过参数memptr返回。

  2. 内存与aligment参数的整数倍对齐,aligment必须是sizeof(void*)与2的整数次幂两两者的乘积。

二、在堆栈上分配内存

  1. allcoa动态分配,不是从堆上分配内存,而是通过增加栈帧大小从堆栈上分配。

  2. 当调用函数的栈帧位于堆栈的顶部,方法可行。帧的上方存在扩展空间,只需修改堆栈指针值即可。

#include<alloca.h>
void *alloca(size_t size);
//size:指定堆栈上分配的字节数。
//返回值为已分配内存块的指针
//不能使用fee释放和不能使用realloc调整内存大小。
//调用alloca造成堆栈溢出,则程序的行为无法预知,特别没有收到NULL返回值通知错误情况下。

void *y;
y = alloca(size);
func(x,y,z);

allcoa分配内存相对于malloc有一定优势:

  1. 速度快;
  2. alloca分配的内存随栈帧的移除自动释。

相关文章:

  • 谷粒商城超详细笔记+踩坑(2)——分布式组件、前端基础(回顾知识点)
  • 为 TiDB 客户端服务端间通信开启加密传输
  • C语言函数解决问题:1.求二进制中不同位的个数;2.交换二进制的奇数位和偶数位;3.使用指针打印数组内容
  • PyQT5入门案例(一)工资统计系统
  • Life:歌曲学习之教一个不会唱歌的人学会唱出《情非得已》、《海阔天空》、《红日》、《老男孩》等歌曲
  • 24.STM32的IO口扩展PCF8574
  • 论文阅读_知识蒸馏_TinyBERT
  • 测试4年,4门语言在手,我拿到了年包50W+的offer
  • Xorm 使用手册,增删改查之查
  • 怎样调试微信小程序
  • T1060 均值(信息学一本通C++)
  • 如何读书
  • 数据分析-非参数秩方法
  • 【JavaWeb篇】使用MyBatis简化JDBC开发|解决SQL语句警告的问题
  • qax特权账号安全能力建设学习咨询
  • hexo+github搭建个人博客
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • express如何解决request entity too large问题
  • Fastjson的基本使用方法大全
  • JavaScript 事件——“事件类型”中“HTML5事件”的注意要点
  • JavaScript设计模式与开发实践系列之策略模式
  • Java新版本的开发已正式进入轨道,版本号18.3
  • JWT究竟是什么呢?
  • Laravel5.4 Queues队列学习
  • learning koa2.x
  • leetcode98. Validate Binary Search Tree
  • SQLServer之创建显式事务
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 利用DataURL技术在网页上显示图片
  • 如何实现 font-size 的响应式
  • 学习使用ExpressJS 4.0中的新Router
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​LeetCode解法汇总307. 区域和检索 - 数组可修改
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #{}和${}的区别是什么 -- java面试
  • (1)(1.13) SiK无线电高级配置(六)
  • (13)Hive调优——动态分区导致的小文件问题
  • (pojstep1.3.1)1017(构造法模拟)
  • (二)springcloud实战之config配置中心
  • (附源码)apringboot计算机专业大学生就业指南 毕业设计061355
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)ssm高校升本考试管理系统 毕业设计 201631
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (论文阅读11/100)Fast R-CNN
  • (十八)三元表达式和列表解析
  • (十六)一篇文章学会Java的常用API
  • (数据结构)顺序表的定义
  • (循环依赖问题)学习spring的第九天
  • (一) storm的集群安装与配置
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • *_zh_CN.properties 国际化资源文件 struts 防乱码等
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • :“Failed to access IIS metabase”解决方法
  • [BZOJ] 2006: [NOI2010]超级钢琴