我最近偶然发现了术语软件老化。 我对此主题的最初想法不太积极,尤其是在阅读了Wikipedia定义之后 。 唯一的流行语是我脑海中唯一能引起共鸣的东西。 但是,在深入研究这个概念之后,我开始有所不同。 即使是我们自己的产品,本质上也可以为软件老化的结果提供保护。 因此,我认为一些概念值得与您分享。
但是,让我们先从维基百科关于该主题的观点开始:
这个定义很无聊。 但是我想大家都记得刚启动的Windows运行良好的日子。 但是在短短几天内,它变得如此缓慢,以至于唯一的解决方案就是重启。 在一年左右的时间里,您需要全新安装,因为重新启动不再对您有帮助。
重新启动和重新安装Windows是一个很好的例子,我想大多数人都可以轻松地与之联系。 甚至甚至同意David Lodge Parnas关于这个问题的看法:
程序像人一样会变老。 我们无法防止老化,但是我们可以了解其原因,采取措施限制其影响,暂时消除其造成的某些损害,并为该软件不再可用的日子做准备。 我们必须全神贯注于第一个版本,并专注于产品的长期健康。
在这句话中,先生。 Parnas还暗示,旧版应用程序更容易老化,但是无论代码库大小如何,您都可能会遭受软件老化的不同原因,例如:
- 内存泄漏(我们目前的面包和黄油)
- 锁争用问题
- 未发布的文件句柄
- 内存/交换空间膨胀
- 资料损坏
- 存储空间碎片
- 舍入错误累积
由于列表过于干燥,因此我将尝试通过引用Java界的例子来增强其功能,以说明原因的相关性(或不相关性)。
内存泄漏 。 这就是我们目前的面包和黄油 –每天我都面临着数十种不同的情况,这些应用程序正遭受泄漏。 事实上,从我们目前的数千个应用程序数据集中,我们可以看到大约50%的应用程序确实包含一个应用程序。 以下示例说明了这种情况。
该程序一次读取一个数字并计算其平方值。 此实现使用原始的“缓存”来存储计算结果。 但是由于这些结果永远不会从高速缓存中读取,因此代码块表示内存泄漏。 如果我们让该程序运行并与用户互动足够长的时间,则“缓存”结果将占用大量内存。 它是老化的一个很好的样本–该程序可以在最终用户受到影响之前使用数天。
public class Calc {
Map cache = new HashMap();
public int square(int i) {
int result = i * i;
cache.put(i, result);
return result;
}
public static void main(String[] args) throws Exception {
Calc calc = new Calc();
while (true)
System.out.println("Enter a number between 1 and 100");
int i = readUserInput(); //not shown
System.out.println("Answer " + calc.square(i));
}
}
}
锁定争用 。 多年以来,您都必须一直处在应用程序运行良好的情况下,然后在负载小幅增加之后,您将开始面临这样的情况:线程开始在同步块后面等待,或者饿死或完全锁定。
以下示例作为案例的教科书插图。 在您启动两个试图同时运行transfer(a,b)和transfer(b,a)并导致死锁的线程之前,代码将正常工作。 再说一次,在类似情况升级为锁定线程之前,您可能会愉快地运行代码数月或数年。
class Account {
double balance;
int id;
void withdraw(double amount){
balance -= amount;
}
void deposit(double amount){
balance += amount;
}
void transfer(Account from, Account to, double amount){
sync(from);
sync(to);
from.withdraw(amount);
to.deposit(amount);
release(to);
release(from);
}
}
未发布的文件句柄 。 再次重申,我确信您在查看类似于以下内容的内容时一直在咒骂,因为其他开发人员在加载后忘记关闭资源。 在java.io.IOException之前,该代码可能已经运行了好几个月了:抛出太多打开文件消息,这再次证明了老化问题。
Properties p = new Properties();
try {
p.load(new FileInputStream(“my.properties”));
} catch (Exception ex) {}
finally {
//no, i will NOT close the stream
}
内存/交换空间膨胀 。 现代操作系统倾向于快速调出一段时间未使用的内存。 因此,当物理内存不足并且操作系统开始交换堆时,您可能会遇到问题。 垃圾收集使事情变得越来越糟–完整GC要求JVM遍历对象图以标识每个可访问的对象以检测垃圾。 这样做时,它将触摸应用程序堆中的每个页面,从而触发页面从内存中换入和换出。
幸运的是,由于多种原因,现代JVM中的影响有所降低,例如:
- 大多数对象永远都不会从年轻一代中逃脱,这些新生代几乎可以保证驻留在内存中
- 从年轻一代移出的对象往往会被频繁访问,这又倾向于将它们保留在常驻内存中。
因此,您可能已经逃脱了这一步,但是由于大量交换,我已经看到GC周期从几百毫秒延长到几十秒。 因此,我们又遇到了一个情况,由于内存膨胀,一段时间后缓存加载缓慢的行为完美的应用程序变成了可用性噩梦。
考虑以上示例–我想您可能同意我的观点,该软件确实确实像人类一样在老化。 我为我们介入救援感到非常高兴。 到目前为止,仅用于解决内存泄漏,但是我可以暗示,在我们的实验室中,我们正在酝酿许多有趣的事情。
翻译自: https://www.javacodegeeks.com/2013/08/why-is-your-software-aging.html