内存受限上的设计模式(1)——内存限额
内存受限下的设计模式(1)——内存限额
背景
在内存受限设备中,经常会遇到这样的问题:
如何在多个相互竞争的组件之间分配内存?
例如,在一部普通的手机中,有游戏、音乐播放、图形界面等组件,每个组件对内存的需求都是“贪婪”的,并且这种“贪婪”是有道理的,即一般情况下,组件得到越多的内存,则该组件的运行表现越良好。但是如果放任每一个组件都去试图尽量多地分配内存,那么系统的总内存将入不敷出。
让我们撇开数量众多的嵌入式设备不说。即便面对如今内存动辄上G的智能手机,我们依然应当谨慎地为各个组件分配内存。
模式
让我们考虑如下的模式:
为每个组件设置限额,超过限额的请求将不予响应。
我们将它称之为:内存限额。
内存限额的目标是防止某一个组件独霸内存。
更深入的探讨
现在,你也许会有这样的疑问,如何确定每个组件的内存限额?
首先需要明确的是,内存限额模式对于内存需求有变化的组件才有意义,如C++中的vector,java中的arraylist。
内存限额的分配应当遵循“小富即安”的原则,即限额应当确保该组件的正常运行即可。有一点原则必须明确:即便某一个组件运行性能特别出色,其他组件也可能成为整个系统性能的瓶颈。内存和CPU的故事,想必无需我再赘述。
适用的场景
警觉的朋友会对这个模式产生疑问。是的,这个模式是一个容易浪费系统内存的模式。即便系统中有空闲内存,某一个组件也可能因为自身的限额而失败。
这个模式适用于负荷稳定、几乎没有弹性负荷的场景,实现简单是它的最大优点。
实现
终于可以脱离纸上谈兵了(*^__^*)
有以下几种方法可供选择,以实现该模式。
示例
下面的C++代码限制MemoryRestrictedClass及其子类所使用的内存总量。如果超额分配,会触发内存不足异常。
下面来看看相对不那么“强大”的java语言如何实现内存限额模式。
是的,java没有提供内存分配和销毁函数,然而限制某一个类的实例数量还是可以做到的,只需要提供一个static的计数器即可。
问题又来了,java如何获取占用内存已被归还?我们可以使用finalize()函数来拦截这一动作,然而,大多数java虚拟机finalize的效率不佳,因此,以下代码仅仅是一个示范,绝非推荐做法(具体请参看Gosling所著的《java语言规范》一书)。
预告
下一篇,介绍小型接口。
背景
在内存受限设备中,经常会遇到这样的问题:
如何在多个相互竞争的组件之间分配内存?
例如,在一部普通的手机中,有游戏、音乐播放、图形界面等组件,每个组件对内存的需求都是“贪婪”的,并且这种“贪婪”是有道理的,即一般情况下,组件得到越多的内存,则该组件的运行表现越良好。但是如果放任每一个组件都去试图尽量多地分配内存,那么系统的总内存将入不敷出。
让我们撇开数量众多的嵌入式设备不说。即便面对如今内存动辄上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; } }
预告
下一篇,介绍小型接口。