Evictor模式描述了何时以及如何释放资源以优化资源管理。这个模式让我们可以配置不同的策略来自动决定哪些资源应该释放,以及应该在什么时候释放。

实例

考虑一个网络管理系统——管理多个网络元素。这些网络元素通常采用拓扑树表示。拓扑树将网络基础设施中的关键元素进行虚拟化层级表现。网络管理系统允许用户查看这样的树,并且可以获得关于一个或者多个网络元素的细节。这个细节可能对应大量的数据(数据量的多少取决于网络元素的类型。),例如,复杂网络元素的细节可能包含了它的状态以及它的组件的状态的信息。

拓扑树的创建时间

1、应用程序启动时;

2、用户要求查看网络元素树时(用户请求);

3、1与2之间的某个时刻。

因此获取网络元素的细节可以是树创建时;用户请求时。但是不管何时保存这些元素细节到内存中,在内存中过长的保存这些细节可能都会代价高昂。因此就会产生两个缺陷场景:

1、某个网络元素的细节再也不会被用户访问,那么它们对于内存资源的消耗将是毫无意义的;

2、某一网络元素的细节会被用户频繁访问,而它有没有被缓存,那就会导致频繁的用于获取该元素的调用,从而导致系统性能的降低;该类细节如果能够保存在内存中,就会增进性能。

所以就延伸出了一个问题:

何时释放资源?释放哪些资源?

Evictor的提出背景Context

需要控制对于资源的释放以确保对资源的高效管理的系统

问题Problem

1、应用程序保存一些只用过几次就不再用的资源而不释放——内存占用、性能下降、系统不稳定;

2、为解决问题1,系统在使用资源后立刻释放资源,这会导致某些高频使用的资源被频繁请求获取,从而引发高昂的请求代价。这些频繁使用的资源应当保存在内存中。

为解决这两个问题,需要考虑几个性质:

①最优性(Optimality):资源的生命周期 受 资源使用的频率 影响;

②可配置性(Configurability):综合资源类型、可用内存、CPU负载 之类的参数来决定资源的释放;

③透明性(Transparency):解决方案对用户透明

解决方案Solution

监控资源的使用,并使用某种策略,如LRU(Least Recently Used,最近最久未使用)、LFU(Least Frequently Used,最近最少使用)等缓存算法来控制其生命周期。每当资源被使用时,就会为应用程序所标记。当资源最近没有被使用,或者不是频繁被使用时,就不会被标记。应用程序会周期性的或者根据实际需要选择那些没有被标记的资源并且释放或者清除它们。而被标记的资源则会被继续留在内存内,因为它们会被频繁使用。

另外,也可以采用其它策略来判断应该清除哪些资源。例如,对于内存受限的应用程序,可以从资源的尺寸来决定应该清除哪些资源。这种情境下,消费大量内存的资源可能会被清除,即使它曾被频繁使用。

Evictor模式的结构Structure

资源使用者:使用资源的实体,可以是应用程序或者操作系统;

资源:可用实体,比如内存、CPU等;

清除者(Evictor):基于一个或多个清除策略来清除资源

清除策略(Eviction Strategy):描述用于判断是否应当清除资源的标准。

动态Dynamics

资源使用者首先获取资源,之后将该资源注册到Evictor,从此时起,Evictor监视资源。当Evictor检测到资源根据清除策略应该被清除时,它就询问资源,判断是否应该清除。如果资源可以被清除,那么就会获得清除资源的机会,之后就会将该资源清除。

大多数资源的清除步骤是一样的,但也存在特殊情况,如资源可能无法被标记,结果就是Evictor会截获资源的请求以获得其清除策略所依赖的统计信息。

实现Implementation

实现Evictor模式涉及4步:

1)定义清除接口。应该定义一个清除接口,所有能够被清除的资源都要实现这个接口。

例如,在Java中的清除接口实现起来可能像这样:

pucblic interface EvictionInterface {
public boolean isEvictable(); //是否可以被清除
public Object info(); //获取同策略相关的信息,以便确定是否清除
public void beforeEviction();//清除前调用。给对象清除它可能获取到的资源的机会
}

例如,EJB Session Bean [Sun04b] 和Entity Bean的接口包含了一个叫做ejbPassivate()的方法,会在Entity Bean或者Session Bean清除之前调用。这就给了Bean释放所有已经获得的资源的机会。

2)决定可清除的资源。开发者必须决定哪些资源可以并且应该被清除。例如,应用程序频繁使用的资源或者无法重新获取的资源就不应该被清除。任何可以清除的资源必须实现清除接口。在清除资源之前,应用程序应该调用这个接口,给资源一个机会来做“善后”工作,这包括把任何必要的状态持久化。

在1)中描述的Java接口中,应用程序可以采用isEvictable()方法来表明资源是否可以被清除。如果返回true,那么Evictor会认为可以考虑把资源清除;如果方法返回false,那么Evictor会忽略此资源

3)决定清除策略。基于应用程序的需求,可以用不同的清除策略来判断是否清除资源,以及清除哪些可以清除的资源。一些用来判断清除那些资源的策略包括LRU、LFU。

此外,可以使用能够接受不同参数的用户定义的策略。例如,清除策略可以考虑重新获取被清除的资源的代价有多昂贵。利用这种策略,重新获取的代价比较低的资源可能会被清除,哪怕它们会比获取起来更昂贵的资源使用的更加频繁。

4)定义系统中对于清除的使用。需要在Evictor中增加清除资源的业务逻辑。这包含判断应该何时以及如何清除资源,以及实际标记要被清除的资源。通常情况下,Evictor在应用程序中作为一个单独对象或者组件而存在,并且会被应用程序以必要的清除策略来配置。例如,应用程序可能会选择在内存低于某个阈值时清除资源。另一个不同的应用程序可能会实现更主动的策略并会周期性地清除资源,即使内存没有低于某个阈值。

Evictor可以使用Interceptor模式[POSA2]来截获用户对对象的访问。Interceptor可以以对资源使用者完全透明的方式来把对象标记为最近正在使用。Evictor会周期性或者根据需要查询所有可清除的对象,以便决定如果需要清除对象的话应该清除哪一些。在前面描述的Java接口中,Evictor会对每个对象调用info()方法并使用获得的信息在清除策略的背景下判断是否应该清除这个对象。对于实际的清除,可以使用Disposal Mehtod模式[Henn03],有时它也叫做Explicit Termination Method模式[Bloc01]。Disposal Method模式描述了类应该如何提供显式的方法来允许它们在被清除之前执行“善后”工作。

实例解析(Example Resolved)

考虑网络管理系统的例子,这个系统负责管理多个网络元素组成的网络。网络元素的细节可以在系统启动时获取,也可以在资源使用者请求时再获取。事先不知道用户意图,所以需要优化资源使用,以便只让那些被资源使用者频繁访问的网络元素保留在内存中。所以这个例子中使用的清除策略是,清除过了某段设定的时间依然没有被资源使用者访问的网络元素。

每个网络元素都需要实现Eviction接口:

public call NetworkElement implements EvictionInterface {

        private NetworkElementComponent [] components ;
private Date lastAccess; public boolean isEvictable() {
return true;
} public Object info() {
return lastAccess;
} public boid beforEviction() {
for ( int i =0; i < components.length; i++) {
components[i].beforeEviction();
}
}
//其他网络元素操作
}

类似的,所有网络元素组件和子组件都需要实现Eviction接口,这样它们被清除时就可以递归地释放所有资源。

Evictor可以实现为一个在自己的线程控制中运行的对象,这样它就能周期性的检查是否有网络元素过了一段特定的时间依旧没被访问。

public class Evictor implements Runnable {
private NetworkElement [] nes;
public Evictor() {
new Thread(this).start();
} public void run() {
while(true) {
try {
Thread.sleep(pollTime);
}
catch(InterruptedException e){
break;
}
for (int i = 0; i < nes.length; i++){
NetworkElement ne = (NetworkElement) nes[i];
if (ne.isEviciable()) {
Date d=(Date) ne.info();
if (d.befor(threshold)) {
ne.beforeEviction();
}
}
}
}
}
}

info()方法返回的信息以及Evictor解释信息的方法是同应用程序相关的,可以按照需要部署的清除策略来裁剪。

变体Variants

Deferred Eviction模式。清除一个或者多个对象的过程可以细化为两步的过程。不是立刻删除对象,而是可以把它们放进一个FIFO队列。当队列满了的时候,队列前边的对象就被清除了。因此,队列又扮演了待清除对象的中间持有者的角色。这和Caching模式有点类似。如果这些对象中的任一个在从队列删除前又被访问了,那么它们就不需要有创建和初始化的开销。当然,这一变体会带来维护队列的开销,而且把清除对象保持在队列中也会占用资源。

Evictor with Object Pool模式。可以用一个对象池来保存被清除的对象。在这个变体中,当对象被清除,它不是从内存中被完整地删除,而只是失去了标识,成为了匿名对象。之后,这个匿名对象会被添加到对象池中,如果对象池满了,那么对象就会被从内存中删除。当需要创建新对象时,队列中的匿名对象就可以出队并且获得新的标识,这就降低了对象创建的开销。对象池的尺寸可以根据可用内存来设置。

Eviction Wrapper模式。可以清除的对象不需要直接实现Eviction接口。可以用实现了Eviction接口的Wrapper Facade[POSA2]来包含对象。Evictor会调用包装对象的beforeEviction()方法,这个方法负责清除实际的对象。这一变体使得整合遗留代码变得简单,不必要求现有的类实现Eviction接口。这个变体的一个例子是在Java中使用对象引用作为包装对象。更多细节可以见“已知应用”一节。

结果Consequences

Evictor模式的优点:

①可伸缩性(Scalability):Evictor模式允许应用程序对使用的资源数目保留一个上限,从而在任何给定的时间内都在内存中。这使得应用程序可以伸缩而不影响总体内存消耗。

②低内存占用(Low memory footprint):Evictor模式允许应用程序通过可配置策略来控制哪些资源应该保留在内存中,哪些资源应该释放。通过只在内存中保留最关键的资源,应用程序可以保持低内存占用、高效运行。

③透明性(Transparency):使用Evictor模式使得资源释放对于资源使用者完全透明。

④稳定性(Stability):Evictor降低了资源枯竭的可能性,从而增加了应用程序的稳定性。

缺点:

①额外开销(Overhead):Evictor模式需要额外的业务逻辑来判断要清除哪些资源,以及实现清除策略。此外,资源的实际清除也可能会带来明显的执行开销。

②重新获取的损失(Re-acquisition penalty):如果再次需要用到被清除的资源,那么就需要重新获取该资源。这可能代价高昂,会影响应用程序的性能。可以通过调整Evictor用来判断清除哪些资源的策略来避免发生这种情况。

已知应用Know Uses

Enterprise JavaBeans(EJB):EJB规约定义了一个activation和deactivation机制,可以被容器用来把bean从内存中换出到次级存储器,从而为其他需要激活的bean释放了内存。bean实例必须实现ejbPassivate()方法,并且释放所有获得的资源。这个方法会被容器在换出bean之前调用到。

.NET:.NET的Coommon Language Runtime(CLR)内部使用了垃圾收集器来释放不用的对象。垃圾收集器根据对象的寿命,分3步给对象归类。如果对象想要在完全清楚之前被通知,那么它必须实现Finalize()方法。.NET建议使用Disposal Method模式的Dispose()方法,客户应该用这个方法来显式地释放对象。

换页:进程所用的内存会被划分成页面。当操作系统需要的内存不够时,不使用的页面就会从内存中清除,并写到磁盘文件上去。当一个页面被再次访问,那么OS就会把需要的信息从磁盘复制到内存。这使得OS可以限制内存中页面数目的上限。换页与通常的交换不同:交换一次清除一个进程的所有页面,而换页则只是清除单独的页面。

资源管理模式:Evictor模式的更多相关文章

  1. 组合模式/composite模式/对象结构型模式

    组合模式/composite模式/对象结构型 意图 将对象组合成树形结构以表示"整体--部分"的层次结构.Composite使得用户对单个对象和组合对象的使用具有一致性. 动机 C ...

  2. 专用服务器模式&共享服务器模式

    连接ORACLE服务器一般有两种方式:专用服务器连接(dedicated server)和共享服务器连接(shared server).那么两者有啥区别和不同呢?下面我们将对这两者的区别与不同一一剖析 ...

  3. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  4. 3.js模式-策略模式

    1. 策略模式 策略模式定义一系列的算法,把它们封装起来,并且可以互相替换. var strategies = { isNonEmpty: function(value,errMsg){ if(val ...

  5. 命令模式/command模式/行为型模式

    举个栗子 指挥官向士兵下达命令,士兵执行 实现代码如下: class Soldier { public void exe() { System.out.println("执行命令" ...

  6. 模板模式与策略模式/template模式与strategy模式/行为型模式

    模板模式 模版模式,又被称为模版方法模式,它可以将工作流程进行封装,并且对外提供了个性化的控制,但主流程外界不能修改,也就是说,模版方法模式中,将工作的主体架构规定好,具体类可以根据自己的需要,各自去 ...

  7. 移动端浏览器隐私模式/无痕模式使用本地存储localStorage/sessionStorage的问题

    移动端浏览器隐私模式/无痕模式使用本地存储localStorage/sessionStorage的问题 开发H5 webapp时经常需要使用本地存储,如localStorage和sessionStor ...

  8. Visitor模式,Decorator模式,Extension Object模式

    Modem结构 Visitor模式 对于被访问(Modem)层次结构中的每一个派生类,访问者(Visitor)层次中都有一个对应的方法. 从派生类到方法的90度旋转. 新增类似的Windows配置函数 ...

  9. delphi 11 编辑模式 浏览模式

    编辑模式 浏览模式 设置焦点 //在使用前需要Webbrowser已经浏览过一个网页 否则错误 uses MSHTML; ///获取Webbrowser编辑模式里面的内容procedure EditM ...

随机推荐

  1. hive 常用日期格式转换

    固定日期转换成时间戳select unix_timestamp('2016-08-16','yyyy-MM-dd') --1471276800select unix_timestamp('201608 ...

  2. django之集成阿里云通信(发送手机短信验证码)

    python3 + django2.0 集成 "阿里云通信" 服务: (SDK文档地址:https://help.aliyun.com/document_detail/55491. ...

  3. from 表单上传多个文件?

    和单个上传文件基本相同,就是需要在后台控制器中,用数组来接收 jsp页面提交过来的file数据. 也分为三个部分演示. 一.jsp <%-- Created by IntelliJ IDEA. ...

  4. Arduino+ESP32 之 驱动GC9A01圆形LCD(一),基于Arduino_GFX库

    最近买了一块圆形屏幕,驱动IC是GC9A01,自己参考淘宝给的stm32的驱动例程, 在ubuntu下使用IDF开发ESP32,也在windows的vscode内安装IDF开发ESP32,虽然都做到了 ...

  5. .NET 5.0 Docker 镜像 错误修复方法

    在给eshopondapr 打镜像的时候碰到了3个错误 1.restore: Received an unexpected EOF or 0 bytes from the transport stre ...

  6. HOOK API(三) —— HOOK 所有程序的 MessageBox

    转载来源:https://www.cnblogs.com/hookjc/ 0x00 前言 本实例要实现HOOK MessageBox,包括MessageBoxA和MessageBoxW,其实现细节与H ...

  7. 对战平台虚拟War3局域网的原理对战平台虚拟War3局域网的原理

    转载请注明来源:https://www.cnblogs.com/hookjc/ 以War3为例,启动魔兽后,首先是如何看见主机的问题:魔兽是通过TCP/UDP协议进行数据发送的,那如何实现看到对方?我 ...

  8. 基于Itextpdf合成PDF

    原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/12023314.html 开发过程中有用到PDF合成, 记录一下合成的方法和代码. 使用工具 : ...

  9. linux内存不足时,为了防止报错,可以使用swap

    1. 创建分区文件, 大小 2G dd if=/dev/zero of=/swapfile bs=1k count=2048000 2. 生成 swap 文件系统 mkswap /swapfile 3 ...

  10. vue/cli的目录结构说明

    node_modules:npm 加载的项目所需要的各种依赖模块. src:这里是我们开发的主要目录(源码),基本上要做的事情都在这个目录里面,里面包含了几个目录及文件: 1.assets:放置一些图 ...