背景
A、B两公司主营相同的业务。A公司耗费两个月时间,开发出一个基于flash的RIA站点用于宣传。B公司耗费一周,开发了一个简单的CMS用于宣传。顾客Z的浏览器不支持flash或禁用了flash。Z先访问了A公司的站点,然后再访问B公司的站点。请问哪家公司的胜算更大些?或者说,哪家公司在竞争中占得了先机?
C公司也耗费两个月时间开发了一个基于JavaScript的RIA站点用于宣传,C公司还多花费了半个月的时间为不支持或禁用了JavaScript的浏览器开发了一个降级版本的站点。
现在,C至少在网络宣传方面将立于不败之地。
Web开发领域中将这种模式称为“优雅降级”或“渐进增强”。嵌入式领域将其称为“局部毁损”。
上面这则故事告诉我们至少三个道理:
1、采用局部毁损模式的产品更可靠;
2、采用局部毁损模式的产品要求更有经验的开发人员和更长的开发周期;
3、搞前端的人可能是和美术接触的比较多,起个名字都这么浪漫——优雅降级;而搞嵌入式显然要更死板更学究气一点——局部毁损。
模式
局部毁损模式要求我们,即便在系统内存耗尽的时候,也要让系统处于安全的状态。
更深入的探讨
局部毁损蕴含着三处值得思考的问题:
Q1:如何确定毁损的时机?
答:当内存耗尽时。一般来说,内存耗尽的消息是直接通过编程语言获知的。C语言通常返回一个错误码来暗示内存已耗尽;Java和C++一般通过异常的方式来通知内存已经耗尽。
Q2:毁损哪一个或多个组件?
答:毁损的一定不是关键的部件。比如我现在在使用javaeye的文本编辑器输入我的博文,则至少应当保证我能输入基本的文字,诸如表情、字体等功能,可以酌情毁损。
Q3:毁损的方式是什么?
答:拒绝申请内存的操作。
Q4:组件如何响应毁损动作?
答:一旦组件侦测到内存分配失败,它必须有能力回复到上一个安全状态,消除因为分配失败所导致的矛盾。有两种策略,适用的对象不同:
1、撤回,即简单地忽略这次内存请求。适用于后台运行的组件。(见下图)
2、降级,即删减不是那么重要又很耗费内存的功能,为关键功能腾出内存空间。适用于用户可见的组件(如UI)。(见下图)
实现
第一步:侦测内存耗尽。
如果从堆中分配内存,则编程语言本身应当会提供某种提醒分配失败的机制。如果使用固定结构分配,那么程序员自己就要保证该组件有能力检查并确定所使用的固定结构已满。
第二步:考虑受影响的组件。
一旦发现内存耗尽,必须确定一个系统中究竟有多少组件受到了这个错误的影响。OO的特性能够减少错误传播的趋势,但是就像我在
《内存受限下的设计模式(2)——小型接口》中叙述的一样,OO在嵌入式领域只是一种选择,但绝非最佳选择。
第三步:释放资源。
在“撤回”(第一幅图)中,为了保证不留下任何副作用,凡是受到错误影响的组件,都要释放已经分配但无法使用的内存,并将其状态恢复至错误发生以前的状态。
C++的异常在try和catch语句之间通常要进行stack unwind。此时C++ run time会调用所有在try块中构造的对象的析构函数。调用的顺序和它们在try中构造的顺序相反。让我们来看下面这个例子(XL C/C++ V8.0 for AIX):
#include <iostream>
using namespace std;
struct E {
const char* message;
E(const char* arg) : message(arg) { }
};
void my_terminate() {
cout << "Call to my_terminate" << endl;
};
struct A {
A() { cout << "In constructor of A" << endl; }
~A() {
cout << "In destructor of A" << endl;
throw E("Exception thrown in ~A()");
}
};
struct B {
B() { cout << "In constructor of B" << endl; }
~B() { cout << "In destructor of B" << endl; }
};
int main() {
set_terminate(my_terminate);
try {
cout << "In try block" << endl;
A a;
B b;
throw("Exception thrown in try block of main()");
}
catch (const char* e) {
cout << "Exception: " << e << endl;
}
catch (...) {
cout << "Some exception caught in main()" << endl;
}
cout << "Resume execution of main()" << endl;
}
输出的结果是:
引用
In try block
In constructor of A
In constructor of B
In destructor of B
In destructor of A
Call to my_terminate
java有自己的垃圾回收机制来释放资源。我们在finally块中书写撤回的代码,从而在清理结束之后恢复至上一个正常状态。请看下面的代码片段:
Command cmd = new Command();
commands.add(cmd);
try{
cmd.execute();
}
finally{
// 恢复至上一个正确状态
commands.remove(cmd);
}
在Symbian中,局部毁损是Symbian基本体系结构的原则之一。Symbian C++不使用标准C++的异常,改用自身提供的TRAP模型。
第四步:降级执行
第三步完成之后,我们可能仍然要确保程序继续执行。“撤回”的做法可以直接跳过此步骤。但“降级”的做法必须在这个步骤中多做一些文章。
比如:
- vista操作系统会在内存不足时自动关闭aero特效(当年1G内存跑vista经常会遇到这个状况)
- firefox浏览器会在页面脚本耗尽资源时弹出对话框提示是否停止执行本页的脚本(你可以自己写一个js的死循环试一下)。
第五步:未雨绸缪。
其实这一步应当算作是第零步才对。
vista在关闭aero开启普通效果时,必须保留足够的内存供普通效果使用。
让我们深入语言细节。即便是异常处理,也需要额外的内存。
因此我们必须保留一些内存以备不测。
示例
Command cmd = new Command();
commands.add(cmd);
try{
cmd.execute();
}
finally{
// 恢复至上一个正确状态
commands.remove(cmd);
}
class Image{
// 创建一个中等画质和小尺寸的图片作为默认图片
static Picture defaultPic = new Picture(Picture.MEDIUM_QUALITY, Picture.SMALL_SIZE);
// 返回默认图片
public static defaultPicture(){
return defaultPic;
}
// 创建图片
public static Picture createPic(int quality, int size){
Picture pic;
try{
pic = new Picture(quality, size);
}
catch(OutOfMemoryException e){
// 内存满,返回默认图片
return defaultPicture();
}
}
}
调用Image.createPic创建高画质大尺寸图片时,即便内存分配失败,也至少可以保证返回一个中等画质和小尺寸的图片。注意如果连defaultPic的内存都分配失败,则该组件根本无法启动。
最后,再次提醒一句:局部毁损模式要求富有经验的程序员,他应当至少知晓:1、何处有毁损风险;2、使用“撤回”还是使用“降级”来实现毁损。
预告
下一篇,介绍欧茨队长。
- 大小: 8.1 KB
- 大小: 9.7 KB
分享到:
相关推荐
侯捷 内存受限系统之软件开发,不用怎么介绍了吧,经典书籍啊 侯捷 内存受限系统之软件开发,不用怎么介绍了吧,经典书籍啊
Thinking Small The Processes for Creating Small Memory Software This paper describes some process patterns for teams to follow when creating software to run in limited memory....It is a draft version ...
适用于有限存储系统(即手持式和专用微型计算设备)的一组连贯且易于使用的模式。 包括C ++和Java中的示例。
基于轻应用的移动学习内容呈现模式研究——以“瀑布流”式布局体验为例 本研究论文主要探讨基于轻应用的移动学习内容呈现模式,特别是以“瀑布流”式布局体验为例。轻应用概念的出现,为我们高效、便捷、低成本地...
Oracle 12c受限模式 只读模式 静默模式 延迟模式
在内存受限的情况下,将数据流按时序进行分割,且用B -树进 行区间分块索引,之后利用RFID数据流统计分布特性进行复杂事件查找与匹配,避免了频繁搜索外存,极大地 降低了I/O开销并提高了吞吐量。此外,进行了相关的...
2 5.10用户查询借书信息 2 5.11用户查询罚款信息 2 5.12借书受限 2 六、小组总结 2 图书馆管理系统——课程设计全文共30页,当前为第2页。 图书馆管理系统——课程设计全文共30页,当前为第2页。 一、系统背景与概述...
UbuntuKylin14.04LTS系统初始配置——更新系统并安装版权受限程序.pdf
11.3内存访问的模式 11.3.1 标记——清扫技术,使用标记位图和延迟清扫 11.3.2 节点复制垃圾收集 11.3.3 渐进式垃圾收集 11.3.4 避免读取 11.4 改进cache性能的标准方法 11.4.1 cache的容量 11.4.2 块大小 11.4.3 ...
This paper describes some UI design patterns to use when creating software to run in limited memory. It is a draft version of a chapter to add to the authors’ book Small Memory Software, and ...
172-387-路网条件受限下过江通道两侧衔接规划研究-word资料.pdf
深入浅出密码学——常用加密技术原理与应用Christof Paar,Jan Pelzl 本书拥有的诸多特征...此外,本书还包含了许多非常新的主题,比如针对受限的应用而优化的轻量级加密( 例如RFID标签或智能卡) ,或新的操作模式。
论文研究-停车受限条件下多模式交通网络动态拥挤收费.pdf, 在一个多起始单终点的交通网络上, 本文研究当终点处停车空间不足时, 如何通过在路段瓶颈处实施拥挤收费实现...
———————————————————————————————————————————————————— 第二步,修改客户端。 单机版中的连接字串符: 取消:CONN.OPEN "provider=Microsoft.jet.OLEDB.4.0;...
上增加3个约束条件来定义受限非负矩阵分解方法的目标函数 ,给出求解受限非负矩阵分解方 法目标函数的迭代规则 ,并证明迭代规则的收敛性.与非负矩阵分解方法相比 ,受限非负矩阵分 解方法能获取尽可能正交的潜在语义....
受限环境下DNS服务发现的研究,易品,魏更宇,物联网的飞速发展将使得数以百亿计的受限设备连入当今的互联网,发现受限网络节点和其相关服务是未来网络必不可少的重要功能。互
本工具可以在64位windows操作系统上解除32位应用程序单进程内存1G的限制
11.3内存访问的模式 11.3.1 标记——清扫技术,使用标记位图和延迟清扫 11.3.2 节点复制垃圾收集 11.3.3 渐进式垃圾收集 11.3.4 避免读取 11.4 改进cache性能的标准方法 11.4.1 cache的容量 11.4.2 块大小 11.4.3 ...
针对资源受限环境对认证和访问控制提出的轻载性、灵活性、基于局部条件的访问策略和端到端的安全性等特殊要求进行了分析和研究,基于拥有证明(proof-of-possession,PoP)安全机制并通过构造PoP权能令牌,提出了一...