Use the heap for dynamic storage

简介:
在C中, 本地变量存储在进程的stack内存区域.
当函数执行完后, 函数体内的本地变量占用的内存会自动释放掉.
在Linux中使用ulimit -a可以查看到stack的限制, 也可以通过/etc/security/limits.conf设置.
[root@db-172-16-3-150 zzz]# ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 204800
max locked memory       (kbytes, -l) 50000000
max memory size         (kbytes, -m) unlimited
open files                      (-n) 131072
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 10240
cpu time               (seconds, -t) unlimited
max user processes              (-u) 131072
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

由于stack区域的内存大小比较小, 所以适合存放临时性的变量. 不适合存放大量的需要hold在内存中的数据.
大数据或者不可预知的数据或者是需要在函数执行完后还需要HOLD在内存中的数据, 可以放到HEAP中。
但是HEAP中的数据与STACK不同,不会自动释放,也没有自动的垃圾回收机制。
如使用malloc申请的内存需要使用free释放, 否则如果没有任何指针指向这块内存区域后, 这块内存将无法回收, 直到进程退出 .
所以建议不用的内存要用free释放掉. 否则容易造成内存泄漏.
另外就是有一个工具valgrind可以用来侦察内存泄漏.
malloc语法
#include <stdlib.h>
void *malloc(size_t size);
// 返回一个空指针. 因为空指针可以存放任何类型的指针数据. 
malloc() allocates size bytes and returns a pointer to the allocated memory.  The memory is not cleared.


malloc与free是一对, free语法 : 
void free(void *ptr);
free() frees the memory space pointed to by ptr, which must have been returned by a previous call to  malloc(),
       calloc()  or realloc().  Otherwise, or if free(ptr) has already been called before, undefined behaviour occurs.
       If ptr is NULL, no operation is performed.


用法举例 : 
场景参照上一篇linked list数据结构的例子.


[root@db-172-16-3-150 zzz]# cat g.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct island {
  char *name;
  char *opens;
  char *closes;
  struct island *next;
} island;

void display(island *start)
{
  island *i = start;
  for (; i != NULL; i = i->next) {
    fprintf(stdout, "Name: %s open: %s - %s\n", i->name, i->opens, i->closes);
  }
}

island* create(char *name)
{
  island *i = malloc(sizeof(island));
  i->name = strdup(name);  // strdup调用了malloc,并且将字符串复制到heap区域, 返回指针.
  i->opens = "09:00";
  i->closes = "17:00";
  i->next = NULL;
  return i;
}

void release(island *start) {
  island *i = start;
  island *next = NULL;
  for (; i != NULL; i=next) {
    next = i->next;
    free(i->name);
    free(i);
  }
}

int main() {
  island *start = NULL;
  island *i = NULL;
  island *next = NULL;
  char name[80];
  for(; fgets(name,80,stdin) != NULL; i = next) {
    next = create(name);
    if (start == NULL)
      start = next ;
    if (i != NULL)
      i->next = next;
  }
  display(start);
  release(start);
  return 0;
}
执行结果 : 
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./g.c -o g && ./g
// 输入
a
b
c
d
// 输入 CTRL+D
// 输出
Name: a
 open: 09:00 - 17:00
Name: b
 open: 09:00 - 17:00
Name: c
 open: 09:00 - 17:00
Name: d
 open: 09:00 - 17:00


其他 : 
1. strdup

      #include <string.h>

       char *strdup(const char *s);
The  strdup()  function returns a pointer to a new string which is a duplicate of the string s.  Memory for the
       new string is obtained with malloc(3), and can be freed with free(3).

2. 在释放struct在heap的空间之前, 必须先通过 这个struct存储的指向heap中字符串的指针去释放这串strdup拷贝到heap的内存区域. 如果struct先释放, 那这块字符串区域将没有任何指针指向它, 也无法回收. 造成内存泄漏.

3. 内存泄漏举例 : 

[root@db-172-16-3-150 zzz]# cat g.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct island {
  char *name;
  char *opens;
  char *closes;
  struct island *next;
} island;

void display(island *start)
{
  island *i = start;
  for (; i != NULL; i = i->next) {
    fprintf(stdout, "Name: %s open: %s - %s\n", i->name, i->opens, i->closes);
  }
}

island* create(char *name)
{
  island *i = malloc(sizeof(island));
  i->name = strdup(name);
  i->opens = "09:00";
  i->closes = "17:00";
  i->next = NULL;
  return i;
}

void release(island *start) {
  island *i = start;
  island *next = NULL;
  for (; i != NULL; i=next) {
    next = i->next;
    // 注释掉这行,也就是不回收调用strdup时申请的内存空间. free(i->name);
    free(i);
  }
}

int main() {
  island *start = NULL;
  island *i = NULL;
  island *next = NULL;
  char name[80];
  for(; fgets(name,80,stdin) != NULL; i = next) {
    next = create(name);
    if (start == NULL)
      start = next ;
    if (i != NULL)
      i->next = next;
  }
  display(start);
  release(start);
  return 0;
}
// 使用gcc -g 编译参数时, 编译的可执行文件包含了 debug 信息. 便于valgrind跟踪时打印详细的信息
[root@db-172-16-3-150 zzz]# gcc -O3 -Wall -Wextra -Werror -g ./g.c -o g && valgrind --leak-check=full ./g
==2166== Memcheck, a memory error detector
==2166== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==2166== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==2166== Command: ./g
==2166== 
// 输入
a
b
c
d
// 输入 CTRL+D 
Name: a
 open: 09:00 - 17:00
Name: b
 open: 09:00 - 17:00
Name: c
 open: 09:00 - 17:00
Name: d
 open: 09:00 - 17:00
==2166== 
==2166== HEAP SUMMARY:
==2166==     in use at exit: 12 bytes in 4 blocks
==2166==   total heap usage: 8 allocs, 4 frees, 140 bytes allocated
==2166== 
==2166== 12 bytes in 4 blocks are definitely lost in loss record 1 of 1
==2166==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==2166==    by 0x345FA798C1: strdup (in /lib64/libc-2.5.so)
==2166==    by 0x400725: main (g.c:23)
==2166== 
==2166== LEAK SUMMARY:
==2166==    definitely lost: 12 bytes in 4 blocks
==2166==    indirectly lost: 0 bytes in 0 blocks
==2166==      possibly lost: 0 bytes in 0 blocks
==2166==    still reachable: 0 bytes in 0 blocks
==2166==         suppressed: 0 bytes in 0 blocks
==2166== 
==2166== For counts of detected and suppressed errors, rerun with: -v
==2166== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 4 from 4)

3. valgrind为什么可以跟踪内存泄漏呢?
因为valgrind自己有malloc和free函数, 并且拦截了进程的malloc和free操作, 使用它自己的malloc和free替代. 进行跟踪.
因此等进程退出的时候, 可以发现有哪些HEAP中的内存是没有被free掉的.
Use the heap for dynamic storage - 德哥@Digoal - The Heart,The World.
 

4. 特别需要注意在heap中的变量, 在修改引用这个地址的指针时, 要先释放掉前面的指针值, 再赋予新的值。否则老的那块内存区域就内存泄漏了.
目录
相关文章
|
Java API 开发者
Spring中@import注解终极揭秘
在Spring框架中,@Import注解可以用来引入一个或多个组件,这些组件通常是通过@Bean注解定义的,当使用@Import注解时,实际上是在告诉Spring:“除了当前配置类中的bean定义外,还想包含另一个配置类(或多个配置类)中定义的bean。”
218 1
Spring中@import注解终极揭秘
|
人工智能 自然语言处理 API
Google Gemma 模型服务:开放的生成式 AI 模型服务
Google Gemma 模型服务:开放的生成式 AI 模型服务
408 4
|
数据采集 存储 数据挖掘
【SPSS】数据预处理基础教程(附案例实战)(下)
【SPSS】数据预处理基础教程(附案例实战)(下)
1058 0
|
安全 Java 持续交付
Java本地远程服务器debug调试
Java本地远程服务器debug调试
504 0
|
运维 Kubernetes Cloud Native
云原生基本概念,核心技术、现状与前景|学习笔记
快速学习云原生基本概念,核心技术、现状与前景
1508 0
云原生基本概念,核心技术、现状与前景|学习笔记
|
7月前
|
敏捷开发 数据可视化 Devops
接口状态自由定制!Apipost、 Apifox和Postman:谁在拖垮你的开发效率
在DevOps盛行的今天,许多团队的接口管理仍停留在传统模式,导致需求延期率飙升34%(Gartner 2023数据)。看似标准的流程可能成为效率杀手,尤其在紧急插入状态时问题凸显。企业级接口管理需满足多环境适配、角色权限隔离、自定义工作流及可视化看板四大需求。对比Apifox、Postman与Apipost三大工具,Apipost以其灵活的状态工厂模式和智能流转规则脱颖而出,支持定制化状态链并自动触发相关操作,助力车联网等企业提升200%协作效率。告别Excel手动维护,开启接口管理新纪元。
|
12月前
|
安全 区块链 数据库
|
数据可视化 数据挖掘 Python
跟着Nature学作图:R语言ggplot2作图展示基因和转座子的相对位置
跟着Nature学作图:R语言ggplot2作图展示基因和转座子的相对位置
|
弹性计算 大数据 测试技术
阿里云4核16G服务器价格多少钱?2024年4核16G阿里云服务器报价及测评
阿里云4核16G服务器价格多少钱?2024年4核16G阿里云服务器报价及测评。阿里云4核16G服务器的价格会根据不同的实例类型和促销活动有所不同。在2024年的价格表中,4核16G的服务器价格有两种说法。一种是作为云服务器ECS的配置,其价格为26元/月或149元/半年。另一种是作为游戏服务器的配置,其价格为26元/月。对于性能评测,4核16G的服务器配置可以提供较高的计算能力和内存容量,适用于处理大型应用和复杂计算任务。无论是网站、应用还是游戏服务器,这种配置都可以提供流畅、稳定的运行体验。
425 0
|
运维 Linux 数据安全/隐私保护
使用Docker compose方式安装Spug,并结合内网穿透实现远程访问
使用Docker compose方式安装Spug,并结合内网穿透实现远程访问