背景
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
分享到:
相关推荐
总的来说,《内存受限系统之软件开发》这本书提供了丰富的理论知识和实践经验,帮助开发者掌握在有限资源下设计和实现高效软件的关键技能。无论是嵌入式系统的开发,还是物联网设备的应用,甚至是移动设备的软件设计...
### 内存受限系统开发——《ThinkinSmall》:用户参与技术处理UI中的内存限制 #### 概述 本文档探讨了在有限内存环境中开发软件时的一些关键问题和设计模式,尤其是在用户界面(UI)的设计方面。对于那些需要运行...
装饰模式是一种设计模式...通过阅读和理解《设计模式:可复用面向对象软件的基础》等经典书籍,以及像博客“设计模式——装饰模式”这样的在线资源,我们可以深入掌握并灵活运用装饰模式,提高代码的可维护性和扩展性。
适配器模式是一种软件设计模式,它允许两个不兼容的接口之间进行通信。在软件工程中,当系统中存在已有的类或组件,但其接口不符合当前系统需求时,适配器模式能起到桥梁的作用,使得旧的组件可以无缝地与新系统协作...
为了更好地理解和分析当前农业电子商务的营销模式,毕慧芳、黄颖所著《农村电商新思维——农业创新营销模式设计》一书详细介绍了“互联网+”农业营销的核心理念、农产品打造之路、品牌农业的发展、农业创新营销策略...
### 内存受限系统软件开发中的UI设计模式与思考小型化方法 #### 一、引言 在现代软件开发过程中,特别是在嵌入式系统或移动设备等资源受限环境中,如何有效地利用有限的内存资源成为了一个重要的挑战。《内存受限...
例如,广西边远地区缺少优质教育资源,传统的教学资源受限于地理位置,难以覆盖到每一个需要的学生。云教室的出现,使得这些地区的教师和学生也能够享受到同步的、高质量的教学资源,学生可以得到更好的教育体验,而...
直销模式下,企业直接面向消费者或零售商,减少了中间环节,提高了反应速度和服务质量,但同时也受限于交通条件和人力物力的投入,可能在某些区域存在销售盲点。 其次,网络式销售适用于大众化产品,娃哈哈和康师傅...
### Android内核驱动——内存...通过上述解析,我们可以看到LowMemoryKiller在Android内核中的重要地位及其精妙的设计思路,它不仅有效地缓解了内存压力,也为Android设备在资源受限环境下的稳定运行提供了有力保障。
### 高级语言程序设计的精髓——算法设计 #### 概述 计算机程序设计语言作为人与计算机交流的重要工具,在计算机科学领域扮演着至关重要的角色。随着计算机技术的飞速发展,程序设计语言经历了从机器语言、汇编...
一个有吸引力的GUI依赖于精心设计的图像和字体。GUI示例越复杂,需要的图像和字体 资源就越多,从而导致消耗更多的内存资源。如果某个设计中所选的MCU没有足够的片内 Flash和片内RAM来存储图像和字体,那么就不得不...
书中还会讨论如何在资源受限的环境下优化这些模式,包括内存管理、效率优化和实时性考虑等方面。此外,作者可能还会介绍如何在C语言中实现面向对象的一些技巧,如模拟继承和多态,以及如何通过封装和模块化来提高...
以上介绍了Oracle 12c中四种特殊的运行模式——受限模式、只读模式、静默模式和延迟模式。这些模式各有其独特的用途,可以帮助DBA们更高效、安全地管理数据库。理解并熟练掌握这些模式对于日常的数据库管理和维护...
斯凯MRP源码——拓展内存使用示例 在IT行业中,特别是在嵌入式系统或者资源有限的设备上,内存管理...通过学习这些内容,开发者可以更好地应对内存受限环境下的软件开发挑战,提升系统性能,从而优化企业的资源管理。
通过理解内存泄露的原因,运用恰当的设计模式和编程技巧,同时借助工具来监控和分析内存使用情况,可以有效地提升应用的性能,延长应用的生命周期,避免因为内存问题导致的崩溃,提升用户的使用体验。
适用于有限存储系统(即手持式和专用微型计算设备)的一组连贯且易于使用的模式。 包括C ++和Java中的示例。
嵌入式系统的特点是资源受限,包括有限的处理能力、内存和存储空间,因此它们通常需要特别的软件设计和优化。软件设计模式是解决软件设计问题的一般性解决方案,它们是模式化的、可重用的,并且具有高度的针对性。在...
标题“基于IP理念的农产品电商平台模式探索——以长泰县为例.pdf”涉及的是利用知识产权(IP)理念对农产品电商平台的模式进行创新性探索,具体以中国福建省长泰县作为研究案例。该研究不仅分析了当前农产品电商的...
基于轻应用的移动学习内容呈现模式研究——以“瀑布流”式布局体验为例 本研究论文主要探讨基于轻应用的移动学习内容呈现模式,特别是以“瀑布流”式布局体验为例。轻应用概念的出现,为我们高效、便捷、低成本地...