如何优化malloc()以充分利用你的记忆?

时间:2015-06-26 12:58:01

标签: c memory-management

我前几天编写了一个小程序来处理单词搜索,发现 bianry搜索树分配内存,我存储了我试图分析的每个单词,使用malloc(),我的4G内存将被快速耗尽。

我的程序中没有内存韭菜,因为我只为该二进制搜索树分配内存。但是,我仍然可以在程序中分配少于6000个二叉搜索树。该二叉搜索树的结构是:

typedef struct BSTnode{
    char data[20];
    struct BSTnode* leftchild;    
    struct BSTnode* rightchild;   
    int num;
}BSTnode;

所以它很小。根据我所学到的,每个结构都需要80字节的内存(data需要20个字节,因为内存对齐也是如此)(对吗?)

6000内存中的结构总共需要花费480MB。

然而,当我尝试为6000个结构分配内存时,我的程序失败了(当我为 5000 分配内存时可以。)我的PC总共有4 GB的内存! (大约有1000MB cached2100MB available1100MB free(根据Windows上的任务经理))。

为什么?

我主要担心的是:

  1. 为什么?

  2. 如何在我的程序中优雅地管理内存分配。

  3. 您能提供更多信息吗?(引用,示例和书籍等)

  4. (顺便说一句,如果你想查看我的代码,请在下面留下评论。行数太多,花一些时间让它更具可读性。抱歉)

    ################################################## ################## 3

    代码:

    #include<stdio.h>
    #include<stdlib.h>
    #include<ctype.h>
    #include<string.h>
    
    typedef struct Node
    {
      struct Node* leftChild;
      struct Node* rightChild;
      char data[20];
      int num;
    } Node;
    
    int inputWord(FILE*, Node*);
    
    int main(int argc, char** argv)
    {
      printf("Enter the name of file you wanna open here:");
      char name[20] =
      { '\0' };
      scanf("%s", name);
    
      FILE* fs = fopen(name, "r");
      if (!fs)
      {
        perror("Failed to open file!");
        exit(EXIT_FAILURE);
      }
    
      Node* firstNode = malloc(sizeof(Node));
      if (firstNode == NULL )
      {
        perror("ALLOCATION FAILED!");
        exit(1);
      }
    
      firstNode->leftChild = firstNode->rightChild = NULL;
      firstNode->num = 1;
      strcpy(firstNode->data, "a");
    
      inputWord(fs, firstNode);
      fclose(fs);
    
      printf("Done!!");
      return 0;
    }
    
    int inputWord(FILE* fs, Node* firstNode)
    {
      rewind(fs);
      /*first figure out a single word, and then put it into to BST*/
      int flag_1 = 0;
      char buf = '\0';
      char word[20] =
      { '\0' };
      Node* ptrOfNode = firstNode;
      int numOfWord = 0;
    
      while (1)
      {
        if (numOfWord < 2000)
        {   //amend this number to determine how many word to be input
          if (1 != fread(&buf, 1, 1, fs))
          {
            perror("failed to read file or eof\n");
          }
          if (!isalpha(buf))
            continue;
          /*this while loop is used to picked out a single word in the text*/
          while (flag_1 == 0)
          {
            strncat(word, &buf, 1);
            if (1 != fread(&buf, 1, 1, fs))
            {
              perror("Failed to read char from the file");
              exit(2);
            }
            if (isalpha(buf))
              flag_1 = 0;
            else
              flag_1 = 1;    //now buf is not alpha
          }
    
          flag_1 = 0;
    
          while (1)
          {
            if (stricmp(word, ptrOfNode->data) > 0&& ptrOfNode->rightChild!=NULL)
              ptrOfNode = ptrOfNode->rightChild;
            else if (stricmp(word, ptrOfNode->data) < 0 && ptrOfNode->leftChild!=NULL)               
              ptrOfNode = ptrOfNode->leftChild;
            else
              break;
          }
       /*the while loop above break for only two reason:
        *1.there have been an identical word in the tree;
        *2.the child where I want to insert the word have not been allocated memory
        */
          if (stricmp(word, ptrOfNode->data) == 0)
          {
            ++(ptrOfNode->num);
            memset(word, '\0', 20);
            ptrOfNode = firstNode;  //move the pointer of Node to the very first
            numOfWord+=1;
            continue;
          }
          else
          {
            if (stricmp(word, ptrOfNode->data) > 0)
            {        //mean that there is no more right child
              ptrOfNode->rightChild = malloc(sizeof(Node));
              if (ptrOfNode->rightChild == NULL )
              {
                perror("FAILED TO ALLOCATED MEMORY!!");
                exit(1);
              }
              ptrOfNode = ptrOfNode->rightChild;
              ptrOfNode->leftChild = ptrOfNode->rightChild = NULL;
              ptrOfNode->num = 1;
              strcpy(ptrOfNode->data, word);
    
              memset(word, '\0', 20);
              ptrOfNode = firstNode;
              numOfWord += 1;
              continue;
            }
            else
            {
              ptrOfNode->leftChild = malloc(sizeof(Node));
              if (ptrOfNode->leftChild == NULL )
              {
                perror("FAILED TO ALLOCATE MEMORY!");
                exit(1);
              }
              ptrOfNode = ptrOfNode->leftChild;
              ptrOfNode->leftChild = ptrOfNode->rightChild = NULL;
              ptrOfNode->num = 1;
              strcpy(ptrOfNode->data, word);
    
              memset(word, '\0', 20);
              ptrOfNode = firstNode;
              numOfWord += 1;
              continue;
            }
          }
        }
        else
          break;
      }
    
      return 0;
    }
    

    我写的另一个程序可以绝对解释我的问题。但这太长了以至于我无法让所有人都阅读并将其发布在此处。[1] https://github.com/walkerlala/searchText

    如果你不认为这是一个适合这个问题的程序(我链接中的那个绝对是),请考虑我上面的问题

1 个答案:

答案 0 :(得分:0)

我写了一些简单的代码来模拟你的问题。

struct Node{
    int val;
    Node *left;
    Node *right;
    Node() :val(1){}
};

int main(){
    int size = sizeof(Node);//size = 12Bytes
    const int N = 10e5;
    const int factor = 5;//12B*5*10^5 = 6MB
    Node* ptrArr[factor];
    //Test 1, costs 57MB!
    for (int i = 0; i < factor; i++){
        ptrArr[i] = new Node[N];
    }
    //Test 2, costs 348MB!
    /*
    for (int i = 0; i < N*factor; i++){
        Node *temp = new Node;
    }*/
    return 0;
}

我们希望分配5*10e5 * Node s,理论上,它会花费12Bytes * 5 * 10e5 = 6MB

我在VS2013中运行此代码,Test 1费用为57MB,而Test 2费用为348MB

回到你的问题,为什么会这样?

  1. 一个原因是片段,另一个原因是保留内存。

    • 如果您打开DEBUG->WINDOWS->MEMORY并查看ptrArr[i]的地址,您会发现在用于保存Node的内存之后,有相当大的未使用区域存储器中。

    • 例如,ptrArr[0] = 0x00b18040ptrArr[1] = 0x0169f0400x0169f040 - 0x00b18040 = 0xb87000 = 12087296 Bytes ≈ 12*10e6 Bytes

    • 因此,Visual Studio分配的内存比我们需要的多10倍。

    • Test 2怎么样?一次分配较小的内存,更多的内存碎片。

  2. 如何在我的程序中优雅地管理内存分配?

    • 避免经常分配小块内存。(它非常慢并且需要更多内存。)
  3. 更多信息。

    • 您知道Visual Studio中std::vector的增加情况吗?
    • std::vector<int> numbers;当我们一次推送一个号码时,numbers的容量会改变如下:
    • 1->2->3->4->6->9->13->19->...->n->(n+n/2)->...
    • 我认为它类似于这个问题:保留额外空间,避免频繁重新分配操作,提高效率。(我不太确定。)
    • 如果您想了解有关操作系统内存管理的更多信息,可以阅读现代操作系统(Tanenbaum)第3章。