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掉的.


 

4. 特别需要注意在heap中的变量, 在修改引用这个地址的指针时, 要先释放掉前面的指针值, 再赋予新的值。否则老的那块内存区域就内存泄漏了.

时间: 2024-10-26 05:47:23

Use the heap for dynamic storage的相关文章

PostgreSQL 10.0 preview 功能增强 - OLAP增强 向量聚集索引(列存储扩展)

标签 PostgreSQL , 10.0 , Vertical Clustered Index (columnar store extension) , 列存储 , 向量聚集索引 背景 未来数据库OLTP+OLAP逐渐模糊化,需求逐渐融合是一个大的趋势,如果你的数据库只支持OLTP的场景,未来可能会成为业务的绊脚石. 在这方面PostgreSQL每年发布的新版本,都给用户很大的惊喜,OLTP已经具备非常强大的竞争力(性能.功能.稳定性.成熟度.案例.跨行业应用等),而OLAP方面,新增的feat

内存管理内幕

动态分配的选择.折衷和实现 Jonathan Bartlett (johnnyb@eskimo.com), 技术总监, New Media Worx 本文将对 Linux 程序员可以使用的内存管理技术进行概述,虽然关注的重点是 C 语言,但同样也适用于其他语言.文中将为您提供如何管理内存的细节,然后将进一步展示如何手工管理内存,如何使用引用计数或者内存池来半手工地管理内存,以及如何使用垃圾收集自动管理内存. 好文章收藏! 原文地址:http://www.ibm.com/developerwork

内存分配器 (Memory Allocator)

对于大多数开发者而言,系统的内存分配就是一个黑盒子,就是几个API的调用.有你就给我,没有我就想别的办法.来UC前,我就是这样认为的.实际深入进去时,才发现这个领域里也是百家争鸣,非常热闹.有操作系统层面的内存分配器(Memory Allocator),有应用程序层面的,有为实时系统设计的,有为服务程序设计的.但他们的目的却是一样的,平衡内存分配的性能和提高内存使用的效率. 从浏览器开发的角度看,手机内存的增长速度相对于网页内容的增长仍然只是温饱水平,像Android本身就是用内存大户,还有一个

介绍J2ME的几个重要概念

概念 J2ME平台中有几个重要的概念,例如内存.CLDC.MIDP等.初学J2ME往往对这些概念理解不深,甚至出现偏差.本文的目的在于对J2ME中的相关重要概念进行阐述. 1. 内存 我们一直在强调,移动信息设备的内存非常小,使用起来应该加倍的珍惜,但是我们却很少知道这些内存是如何分类的,下面将做详细的介绍.事实上MIDP设备的内存分为三种,Programme Memory.Heap.persistent Storage. Programme Memory是移动信息设备分配给MIDlet sui

J2ME平台的几个重要概念

J2ME平台中有几个重要的概念,例如内存.CLDC.MIDP等.初学J2ME往往对这些概念理解不深,甚至出现偏差.本文的目的在于对J2ME中的相关重要概念进行阐述. 1. 内存 我们一直在强调,移动信息设备的内存非常小,使用起来应该加倍的珍惜,但是我们却很少知道这些内存是如何分类的,下面将做详细的介绍.事实上MIDP设备的内存分为三种,Programme Memory.Heap.persistent Storage. Programme Memory是移动信息设备分配给MIDlet suite的

深入理解C++中变量的存储类别和属性_C 语言

C++变量的存储类别(动态存储.静态存储.自动变量.寄存器变量.外部变量)动态存储方式与静态存储方式 我们已经了解了变量的作用域.作用域是从空间的角度来分析的,分为全局变量和局部变量. 变量还有另一种属性--存储期(storage duration,也称生命期).存储期是指变量在内存中的存在期间.这是从变量值存在的时间角度来分析的.存储期可以分为静态存储期(static storage duration)和动态存储期(dynamic storage duration).这是由变量的静态存储方式和

Formation:你有PB级的限制存储?

Formation是一家初创公司,创建于2013年,在两轮融资中累积获得2740万美元. Formation Data Systems公司董事长和首席执行官是Mark Lewis,32年前他从DEC存储工程开始做起,曾经在Compaq.EMC.EMC Ventures.Riverbed董事会任职,在2013年创建Formation并担任董事长和首席执行官之前,他在Silver Lake担任高级顾问.他的共同创始人是Andy Jenks,曾经在Khosla Ventures以及EMC Ventur

关于内存的最后一个难点--the paged and the non-paged pool

很怀疑下面的英文单词precent and procent都是PERCENT的笔误. 也就是这两个POOL,都可以增长,都是在可用内存里的. 但区别是PAGED增长到LIMIT后,会移入PAGEFILE.SYS. 而NON-PAGED长驻内存,增长到LIMIT后可能系统会出现异常. 然后,它们都只会占用一定比例的可用内存. 最后,问题来了:既然NONE-PAGED长驻内存,那为什么又要计入可用内存呢???不解ING~~~ What are memory heaps and what are th

使用Kubernets Pet Set部署上千个Cassandra实例

本文讲的是使用Kubernets Pet Set部署上千个Cassandra实例[编者的话]本文是Kubernetes1.3 五日谈中的一篇文章,主要介绍Pet Set以及使用Pet Set部署上千个Cassandra实例,通过模拟古希腊巨兽之间的赛跑数据,验证Cassandra集群的可用性.译者在结尾处简单介绍了Pet Set,不了解的同学可以先看看(主要是因为译者不太了解,欢迎熟悉的同学批评指正). 古希腊巨兽赛跑比赛(The Greek Pet Monster Races) 随着Kuber