python减少内存占用,Python的浮点类型没有长度限制,只受限于内存的大小

  python减少内存占用,Python的浮点类型没有长度限制,只受限于内存的大小

  在实际项目中,很多地方都有IO。

  木卫一需要时间。比如新malloc。

  对于现代电脑来说,往往是GHz主频,似乎没有什么影响。但是在大型项目中,重复new和delete不仅会耗费大量时间,还会导致内存碎片。

  新是一件很麻烦的事情。

  哪里有新,哪里就是你可能埋坑的地方。

  new发出的内存无法正确释放,会导致内存泄漏。一个细微的内存泄漏,日积月累就会让程序崩溃。

  掌握内存管理是C程序员必备的能力。

  其次,这里实现了一个简单的内存池。

  这个内存池和STL的内存池没法比。STL的内存池实现更复杂,当然也提供了更强大的功能。但其本质是一样的。这个简单的内存池用于模板。

  必备知识点:

  c基本语法:reinterpret_cast(para))))))))))))))。

  结构:链表

  c通用编程:模板

  操作系统:关键区域,锁定

  放置新的(需要自己看C Primer))。

  所以,我们开始吧:

  实践中的一个基本点:reinterpret_cast(para))))))))))。

  看一下reinterpret_cast(para)的字面意思,重新解释一下。你如何解释深刻的话?在《C++ Primer》这本书里,对此的解释很少。

  以前不知道,看多了就有感觉了。我的理解是用新的expr结构重新解读。

  在下面的代码中,您将看到以下内容

  phead=*(reinterpret _ cast)(phead))))).

  其实pHead是T*型的。在reinterpret_cast中,T*结构的pHead被强制转换成T**结构,然后在T**结构上执行*运算符。怎么样?很暴力。之后用代码解释。

  练习点2:数据结构:链表

  熟悉基本的数据结构必须是所有程序员的必备技能。我常听鲜艳的荷花。基础决定了你能爬多高。想想吧。一座高楼从平地上拔地而起。高二学数据结构的时候,完全不知道老师在讲台上一个人吐槽是什么意思,也不知道他在说什么。随着考试的临近,我在努力看书,对数据结构在说什么有了一些想法。好吧,让我们回到正题。

  所谓链表是指有头的节点H,指向下一个节点NodeA,NodeA指向它的下一个节点nodeb,直到最后一个节点指向空值。这样就成了一系列俗称的链表。

  例如:

  类型节点

  {

  int _ content

  节点*下一个;

  }

  节点*头;//定义一个链表头

  Node * pNode//定义另一个节点

  pHead-next=pNode;//头指向另一个节点.

  上面的代码是一个简单的例子,省略了初始化部分。这个在实践中是写不出来的。

  实践要点3: C通用编程:模板

  基于模板的APP应用的一个典型例子就是STL。所以使用vector、list、map等容器很容易。STL已经成为C语言的重要库之一,并在工程中得到广泛应用。忽略此类型的定义可以提高代码的可重用性。模板在网络编程中的作用至关重要。

  模板在书《C++ Primer》中有非常详细的解释。模板函数,模板类。如果你不知道,请仔细阅读这本书的通用编程部分。

  练习点4:操作系统:临界区,摇滚

  现代计算机的硬件已经非常普及。多核多线程。为了充分发挥处理器的作用,多线程技术在实际工程中已经相当成熟。但是在多线程操作的情况下,线程A写入一些共享数据,线程B也写入相同的共享数据。如果控制不好,共享的数据就会变成脏数据。这时候我该怎么办?有各种各样的解决方案,如锁定、互斥变量、信号量和临界区。Windows提供了一系列API来处理这些内容。我们需要使用

  初始化安全(critical _ section *;//初始化限制区域

  deletecriticalsection(critical _ section *;//解放边界地区

  企业安全(critical _ section *;//进入临界区

  leavecriticalsection(critical _ section *;//离开临界区

  其中,CRITICAL_SECTION是Windows中定义的关键区域结构。我们不必深究,只需知道它的作用。

  处理共用面积的整个过程应该如下:

  共享数据在初始化期间声明并初始化关键部分结构。当线程A操作共享数据时,它

  等等,让数据进入临界区,然后操作。这样,线程b就无法写入数据。完成后,让数据离开关键区域。b线程此时只能写数据。当数据被破坏时,关键区域被释放。

  介绍完这些知识点,下面就是编码的时间了。在命名中,我们给关键区域一个单独的名字:锁。

  定义临界区(因为你要对Windows使用API函数,记得包括Windows.h)

  CBaseLock类

  {

  公共:

  CBaseLock()

  {

  InitializeCriticalSection(m _ Sect);

  }

  ~CBaseLock()

  {

  DeleteCriticalSection(m _ Sect);

  }

  无效锁()

  {

  enter critical section(m _ Sect);

  }

  无效解锁()

  {

  LeaveCriticalSection(m _ Sect);

  }

  私人:

  临界_截面m _截面;

  };

  定义锁

  模板

  类别CLockImpl

  {

  公共:

  CLockImpl(T* l) : m_lock(l)

  {

  m _ Lock-Lock();

  }

  ~CLockImpl()

  {

  m _ lock-Unlock();

  }

  私人:

  T * m _ lock

  };

  定义内存池

  模板

  MyPool类

  {

  公共:

  我的池()

  {

  allocated count=0;

  ElemSize=sizeof(T) sizeof(T*)?sizeof(T):sizeof(T *);

  pHead=NULL

  }

  ~我的池()

  {

  while(pHead)

  {

  T * ret=pHead

  pHead=*(reinterpret _ cast(pHead));

  免费(退休);

  }

  }

  int GetCount()常量

  {

  返回numAllocated

  }

  t *分配()

  {

  CLockImpl锁(m _ lock);

  numAllocated

  if(pHead==NULL)

  return new(malloc(ElemSize))T;

  T * ret=freeListHead

  pHead=*(reinterpret _ cast(pHead));

  返回新的(ret)T;

  }

  模板

  T *alloc(常数T1 p)

  {

  CLockImpl锁(m _ lock);

  numAllocated

  if(pHead==NULL)

  返回new(malloc(ElemSize))T(p);

  T * ret=pHead

  pHead=*(reinterpret _ cast(pHead));

  返回新的(ret)T(p);

  }

  模板

  模板

  …//这些情况我就不写了。

  无效dealloc(T* elem)

  {

  elem-~ T();

  如果(真)

  {

  CLockImpl锁(m _ lock);

  memset(elem,0xfe,elementSize);

  -allocated count;

  *(reinterpret _ cast(elem))=pHead;

  pHead=elem

  }

  }

  私人:

  int AllocatedCount//分配的内存节点数

  size _ t ElemSize//内存节点的大小

  T * pHead//空闲内存的链表头

  LockMode m _ lock

  }

  上面的代码基本上完成了一个简单的内存池。

  下面,对内存池中的一些关键语句进行解释。

  构造器

  当内存初始化时,什么都没有。因此,指向空闲内存区域的指针应该指向NULL。分配的存储器节点的数量当然必须是0。最深刻的,应该是这句话:

  ElemSize=sizeof(T) sizeof(T*)?sizeof(T):sizeof(T *);

  这意味着ElemSize的大小应该是两个参数T和T*中较大的一个。(为什么要这样设计?往下看。)

  接口PublicAlloc()

  对于内存池中的element对象,如果需要多个参数进行初始化,则需要传入多个参数。这样,alloc()接口显然需要满足很多情况,所以需要将alloc()定义为模板函数,并提供相应的结构参数。形式参数是在内存池中用模板参数指定的,因此通过调用alloc()并传入实际参数,可以初始化元素并为它们分配内存。

  仔细看alloc()的实现,发现当pHead指向null时,需要一个ElemSize大小的新空间。当pHead不为空时,返回pHead指向的空间。

  pHead=*(reinterpret _ cast(pHead));

  pHead指向T* type,然后用reinterpret_cast重新解释PHead。此时,pHead指向的地址的空间(逻辑)结构发生变化。假设pHead指向ElemA类型,sizeof(ElemA)=5,用reinterpret_cast重新解释,pHead指向一个指向指针的指针。那么*reinterpret_cast就是重新解释的地址区的值(是什么?继续往下看)。

  破坏者

  结构中,有这样一句话:

  *(reinterpret _ cast(elem))=pHead;

  按照前面的解释,elem的地址用T**重新解释,然后取值把pHead指向的地址放在那里。并且pHead指向的地址必须大于T和T*的地址。在32位操作系统下,T*是4个字节,也就是说pHead指向的地址至少有4个字节,足够容纳至少一个T**类型。也就是说我删除了ELM的内存的内容,然后把地址分成几个4字节的连续块(32位系统下),把pHead指向的第一个空闲地址块的地址下放到电阻块的第一个4字节保存,那么pHead=elemPHead指向elem的地址,那么第一个空闲地址就是新释放的elem的地址,elem地址中的前4个字节保存下一个空闲地址块的地址.所以链接成一个链表。

  然后回到alloc,alloc首先检查是否有空闲地址,也就是pHead是否指向NULL,如果指向NULL,就会新建一个ElemSize大小的内存。在这个内存中,它将使用放置新技术在请求的内存中构造所需的元素对象。如果pHead没有指向NULL,说明已经有应用的内存了(可能是某个对象被析构了,留下了,没有返回给系统)。获取pHead指向的第一个空闲内存块的地址,然后pHead指向这个空闲地址指向的空闲地址块(即pHead=*(reinterpret _ cast(pHead));这句话的意思),然后构造元素对象。这样就减少了思考新系统的次数,节省了系统寻找合适大小内存块的时间,提高了效率。

  锁定的原因是防止多个线程同时在一个对象内存池上操作。

  再次解释这个简单的内存池。池子的运行机制大致如下。有些内存池会比这个复杂得多。但是了解了分配的关键点之后,任何内存池都可以玩的游刃有余。

  总结:

  内存池的存在减少了系统IO次数和系统寻找合适大小的内存块的时间。提高了程序的运行效率,有效减少了内存碎片的产生。

  =====结尾=====

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: