控制结构(6): 最近最少使用(LRU)
// 上一篇:必经之地(using)
// 下一篇:程序计数器(PC)
基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构。
There are only two hard things in computer science: cache invalidation and naming things.
--Phil Karlton
前情回顾
上一次,我们写了资源打开关闭自动化的控制结构。有熟悉ObjectC语言的朋友补充了一个苹果的做法: autorelease pool
比如说Lock,可以这么声明:
@autoreleasepool{
id lock = [Lock lock]; // autorelease 对象, 创建后 lock, dealloc 时释放
}
它的主要作用是,延迟对象的释放。对一个对象调用 autorelease 方法,那么它就被保存到最里层的 autorelease pool。当 autorelease pool 块结束的时候,对里面所有的对象发送一次 release。Objective-C 里有大量的对象都是通过 autorelease pool 管理。
也可以通过autoreleasepool做性能优化:
for(int i=0; i<100; i++){
create_lots_autoreleased_objects();
}
这段代码可以被优化成:
for(int i=0; i<100; i++){
@autoreleasepool{
create_lots_autoreleased_objects();
}
}
这个相当于是加速了内存的释放流程,降低了峰值内存占用。那么,其他语言有这种便利么?
典型代码
function send(pkg, onComplete){
let connection = new Connection(name);
connection.open(function(err){
connection.send(pkg, function(err){
connection.close();
onComplete();
});
})
}
结构分析
又是open,close。当然可以使用前一节讲到的方法。但这不是本节的主题,前一节实际上做了一个潜在假设,就是存在一个连接池子,下面的open和close里面获取资源和释放资源都是从资源池里取出和放回的:
open(onComplete) {
let self = this;
/**get from pool*/
self.m_database.getPOOL().getConnection(function(err, conn) {
if (err) {
return onComplete(err);
}
self.m_conn = conn;
onComplete(0);
});
}
close() {
let self = this;
/**release to pool*/
self.m_conn.release();
}
如果你的资源不是个数据库,例如一个TCP连接,那么open和close是实实在在的打开和关闭连接,频繁操作就有性能问题。所以我们需要自己定制一个连接池。例如下面的实现:
class ConnectionPool{
constructor(capacity,ttl){
this.m_pool = {};
this.m_capacity = capacity;
this.m_ttl = ttl;
}
init(){
let self = this;
base.setTimer(function(){
self.trimeByTimeout();
}, 30*1000);
}
trimeByTimeout(){
let self = this;
/**剔除超时的连接*/
for(let name in self.m_pool){
let item = self.m_pool[name];
let elapse = base.getNow()-item.updateTime;
if(elapse>self.m_ttl){
self.remove(item);
}
}
}
getConnection(name,onComplete){
let self = this;
let item = self.peek();
self.open(item,(err){
onComplete(err, item.conn);
});
}
peek(name){
let self = this;
let cache = self.getCache(name);
if(cache){
return cache;
}
self.trimeByLRU();
let conn = self.create(name);
let item = self.insert(name, conn);
return item;
}
getCache(name){
let self = this;
/**获取缓存,也可以加入使用频次的统计,根据使用频次来选取*/
let cache = self.m_pool[name];
if(cache){
cache.updateTime = base.getNow();
return cache;
}else{
return null;
}
}
trimeByLRU(){
let self = this;
/**剔除最不活跃的连接*/
if(self.overflow()){
let minItem = self.findMinItem();
if(minItem!=null){
self.remove(minItem);
}
}
}
overflow(){
let self = this;
let length = Object.keys(self.m_pool).length;
return length > self.m_capacity;
}
findMinItem(){
let self = this;
let min = Infinity;
let minItem = null;
for(let name in slef.m_pool){
let item = self.m_pool[name];
if(item.updateTime<min){
min = item.upddateTime;
minItem = item;
}
}
return minItem;
}
create(name){
let conn = new Connection(name);
return conn;
}
open(item, onComplete){
let self = this;
if(item.refCount===0){
item.conn.open(onComplete);
}else{
item.refCount++;
onComplete(0);
}
}
release(item){
let self = this;
item.refCount--;
if(item.refCount===0){
if(item.markRemove){
delete self.m_pool[item.name];
item.conn.close();
}
}else{
// ignore
}
}
insert(name, conn){
let self = this;
let item = {
updateTime: base.getNow(),
conn: conn,
refCount: 0,
markRemove: false,
name: name,
}
/**给conn添加release方法*/
item.conn.release = function(){
self.release(item);
}
self.m_pool[name] = item;
return item;
}
remove(item){
let self = this;
if(item.refCount>0){
item.markRemove = true;
}else{
delete self.m_pool[item.name];
item.conn.close();
}
}
}
有了连接池,就可以像常规一样使用Connection:
function test(){
let pool = new ConnectionPool(32,30*1000);
pool.init();
pool.getConnection(function(err, conn)=>{
conn.connect(function(err){
//...
conn.release();
})
});
}
语义分析
Least Recently Used, 简称LRU,是一种常用的缓存思路。基本思想是从资源池里剔除最近最少使用的资源条目。
编程中经常有优化的需求,例如缓存。但是很容易出现所谓的碎片优化,例如在需要使用资源的地方,每个地方都去手工持有资源的缓存,就不是一种好的方法,如果有多个协作者,还会导致每个人都自己做了一份缓存,或者在源代码的多个地方对同一类资源重复做缓存。
正确的做法应该是:
- 使用资源的地方保持资源使用的最简单形式:打开,使用,关闭(当然你可以使用自动化手法)
- 提供统一并且单点的资源池实现,在一个地方把资源的缓存做好。
控制结构(6): 最近最少使用(LRU)的更多相关文章
- 控制结构(6) 最近最少使用(LRU)
// 上一篇:必经之地(using) // 下一篇:程序计数器(PC) 基于语言提供的基本控制结构,更好地组织和表达程序,需要良好的控制结构. There are only two hard thin ...
- Redis学习笔记2-使用 Redis 作为 LRU 缓存
当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...
- Redis - 作为 LRU 缓存
一.简介 LRU 实际上是被唯一支持的数据移除方法,同时也是 memcached 默认支持的缓存算法. 二.配置内存大小 在 redis.conf 文件中使用 maxmemory 指令能够配置内存大小 ...
- LeetCode解题报告:LRU Cache
LRU Cache Design and implement a data structure for Least Recently Used (LRU) cache. It should suppo ...
- 控制结构(7) 程序计数器(PC)
// 上一篇:最近最少使用(LRU) // 下一篇:线性化(linearization) 程序的每一行都是一个状态,对应的行指令.同步的情况下同一个pc一直自增,异步的时候,分裂出一个新的子pc,独立 ...
- Redis作为lru缓存作用
当 Redis 作为缓存使用时,当你添加新的数据时,有时候很方便使 Redis 自动回收老的数据.LRU 实际上是被唯一支持的数据移除方法.Redis 的 maxmemory 指令,用于限制内存使用到 ...
- 数据结构与算法之美 06 | 链表(上)-如何实现LRU缓存淘汰算法
常见的缓存淘汰策略: 先进先出 FIFO 最少使用LFU(Least Frequently Used) 最近最少使用 LRU(Least Recently Used) 链表定义: 链表也是线性表的一种 ...
- mysql5.5手册读书日记(1)
<?php //mysql语句使用技巧 /* * 我的数据库是5.5.2 * * 查询当前用户的登陆的名字 * select user(); * * 查询当前mysql服务器时间和服务器版本 * ...
- feilong's blog | 目录
每次把新博客的链接分享到技术群里,我常常会附带一句:蚂蚁搬家.事实上也确实如此,坚持1篇1篇的把自己做过.思考过.阅读过.使用过的技术和教育相关的知识.方法.随笔.索引记录下来,并持续去改进它们,希望 ...
随机推荐
- 史上最走心webpack4.0中级教程——配置之外你应该知道的事
<webpack4.0各个击破系列>适合不满足于只会配置webpack但一时间又看不懂源码的中级读者.我没法保证这个系列是最好的,但至少能保证每一篇博文都跟那些Ctrl+C和Ctrl+V的 ...
- IOS中armv7,armv7s,arm64以及i386和x86_64讲解
一.前言问题 在iOS 开发过程中,估计比较少的人会在意armv7,armv7s,arm64这些概念,如果在意可能也是项目中出现了像下面的问题,才会想起来解决这些问题,但还是不是特别的理解,这些概念, ...
- C#实现窗体全屏
方法一:设置窗体属性 //程序启动路径,与生成程序的exe文件在同一目录下 public String exePath = Application.StartupPath; //定义窗体宽高 ; ; ...
- 弹框在UC浏览器或者Android机器上会被顶上去
弹框在UC浏览器或者Android机器上会被顶上去 可以通过监听resize事件 this.height = $(document).height(); window.addEventListener ...
- 广州.NET微软技术俱乐部休闲活动 - 每周三五晚周日下午爬白云山活动
基于如下原因: 正如我们在<广州.NET微软技术俱乐部与其他技术群的区别>里面提到的:有人在活动中表达"少了一点自由交流的时间, 我们来自五湖四海, 希望多点时间彼此认识&quo ...
- 安卓手机如何快速投屏到windows(10/8.1/7)电脑上
前提: 手机和电脑连接的网络必须在同一局域网下. 优势: 手机和电脑不需要下载对应平台的应用,完全使用全系统自带功能. 附加: 以下演示是安卓手机和windows操作系统电脑,并且win10和win1 ...
- 【English】二、It作为代词,可以代指什么
it的用法 一.用作人称代词: 1.指代事物: — What’s this? — It’s a cat.2.指代人:常用于不知对方性别时,比如:询问敲门人或打电话时询问对方是谁,或者用来指代婴儿. ( ...
- DedeCMS清空删除所有文档后新建文档信息ID从1开始
方法一.登录织梦后台,找到系统->系统设置->SQL命令行工具 分别运行以下命令: 清除表中的数据,删除所有文章: truncate table `dede_arctiny`; trunc ...
- Typora 快捷键
今天学习了一下这个工具.很轻便,很好用的. 无序列表:输入-之后输入空格 有序列表:输入数字+“.”之后输入空格 任务列表:-[空格]空格 文字 标题:ctrl+数字 表格:ctrl+t 生成目录:[ ...
- Spark资源调度和任务调度
一.资源调度&任务调度 1.启动集群后,Worker节点会周期性的[心跳]向Master节点汇报资源情况,Master掌握集群资源情况. 2.当Spark提交一个Application后,根据 ...