`
zhc0822
  • 浏览: 228666 次
  • 性别: Icon_minigender_1
  • 来自: 宝仔的奇幻城堡
社区版块
存档分类
最新评论

内存受限下的设计模式(1)——内存限额

阅读更多
背景
在内存受限设备中,经常会遇到这样的问题:
如何在多个相互竞争的组件之间分配内存?
例如,在一部普通的手机中,有游戏、音乐播放、图形界面等组件,每个组件对内存的需求都是“贪婪”的,并且这种“贪婪”是有道理的,即一般情况下,组件得到越多的内存,则该组件的运行表现越良好。但是如果放任每一个组件都去试图尽量多地分配内存,那么系统的总内存将入不敷出。
让我们撇开数量众多的嵌入式设备不说。即便面对如今内存动辄上G的智能手机,我们依然应当谨慎地为各个组件分配内存。

模式
让我们考虑如下的模式:
为每个组件设置限额,超过限额的请求将不予响应。
我们将它称之为:内存限额。
内存限额的目标是防止某一个组件独霸内存。

更深入的探讨
现在,你也许会有这样的疑问,如何确定每个组件的内存限额?
首先需要明确的是,内存限额模式对于内存需求有变化的组件才有意义,如C++中的vector,java中的arraylist。
内存限额的分配应当遵循“小富即安”的原则,即限额应当确保该组件的正常运行即可。有一点原则必须明确:即便某一个组件运行性能特别出色,其他组件也可能成为整个系统性能的瓶颈。内存和CPU的故事,想必无需我再赘述。

适用的场景
警觉的朋友会对这个模式产生疑问。是的,这个模式是一个容易浪费系统内存的模式。即便系统中有空闲内存,某一个组件也可能因为自身的限额而失败。
这个模式适用于负荷稳定、几乎没有弹性负荷的场景,实现简单是它的最大优点。

实现
终于可以脱离纸上谈兵了(*^__^*)
有以下几种方法可供选择,以实现该模式。
  • 重写内存管理函数。比如,你可以重写c++中的new和delete方法。
  • 分离的heap。可以让每个组件各自使用分离的heap。windows系统的操作系统支持该操作,至少我知道的,windows CE是肯定支持的。
  • 分离的进程。大部分的linux允许开发者为每一个进程指定一个内存限额。很方便,不是吗?


示例
下面的C++代码限制MemoryRestrictedClass及其子类所使用的内存总量。如果超额分配,会触发内存不足异常。
class MemoryRestrictedClass{
     public:
          enum { LIMIT_IN_BYTES = 10000 };
          static size_t totalMemoryCount;
          void* operator new(size_t aSize);
          void* operator delete{void* anItem, size_t aSize};
};

size_t MemoryRestrictedClass::totalMemoryCount = 0;

// 实现new操作符
void* MemoryRestrictedClass::operator new(size_t aSize){
     // 如果分配后内存大于限额,抛出内存不足异常
     if ( totalMemoryCount + aSize > LIMIT_IN_BYTES )
          throw (bad_alloc());

     // 更新计数器,增加之
     totalMemoryCount += aSize;
     return malloc(aSize);
}

// 实现delete操作符
void* MemoryRestrictedClass::operator delete(void* anItem, size_t aSize){
     // 更新计数器,减少之
     totalMemoryCount -= aSize;
     free((char*)anItem);
}


下面来看看相对不那么“强大”的java语言如何实现内存限额模式。
是的,java没有提供内存分配和销毁函数,然而限制某一个类的实例数量还是可以做到的,只需要提供一个static的计数器即可。
问题又来了,java如何获取占用内存已被归还?我们可以使用finalize()函数来拦截这一动作,然而,大多数java虚拟机finalize的效率不佳,因此,以下代码仅仅是一个示范,绝非推荐做法(具体请参看Gosling所著的《java语言规范》一书)。
Class RestrictedClass{

     // 最大可分配10个实例
     static final int maxNumberOfInstances = 10;
     // 伟大的计数器又出现了
     static int numberOfInstances = 0;

     public RestrictedClass(){
          numberOfInstances++;
          if ( numberOfInstances > maxNumberOfInstances ){
               // 出师未捷身先死,直接被回收掉...
               System.gc();
               throw new OutOfMemoryException("只能创建10个对象");
          }
          
     }

     public void finalize(){
          --numberOfInstances;
     }
}


预告
下一篇,介绍小型接口。
1
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics