Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
2d52516f
提交
2d52516f
authored
7月 30, 2012
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
LIRS replacement algorithm
上级
dbce1ba0
隐藏空白字符变更
内嵌
并排
正在显示
2 个修改的文件
包含
561 行增加
和
116 行删除
+561
-116
TestCache.java
h2/src/test/org/h2/test/store/TestCache.java
+309
-0
CacheLirs.java
h2/src/tools/org/h2/dev/store/btree/CacheLirs.java
+252
-116
没有找到文件。
h2/src/test/org/h2/test/store/TestCache.java
0 → 100644
浏览文件 @
2d52516f
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package
org
.
h2
.
test
.
store
;
import
java.util.HashMap
;
import
java.util.Random
;
import
org.h2.dev.store.btree.CacheLirs
;
import
org.h2.test.TestBase
;
import
org.h2.util.New
;
/**
* Tests the cache algorithm.
*/
public
class
TestCache
extends
TestBase
{
/**
* Run just this test.
*
* @param a ignored
*/
public
static
void
main
(
String
...
a
)
throws
Exception
{
TestBase
.
createCaller
().
init
().
test
();
}
public
void
test
()
throws
Exception
{
testEdgeCases
();
testGetPutPeekRemove
();
testLimitHot
();
testLimitNonResident
();
testBadHashMethod
();
testScanResistance
();
testRandomOperations
();
}
private
void
testEdgeCases
()
{
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
0
);
test
.
put
(
1
,
10
);
assertEquals
(
10
,
test
.
get
(
1
).
intValue
());
}
private
void
testGetPutPeekRemove
()
{
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
4
);
test
.
put
(
1
,
10
);
test
.
put
(
2
,
20
);
test
.
put
(
3
,
30
);
assertNull
(
test
.
peek
(
4
));
assertNull
(
test
.
get
(
4
));
test
.
put
(
4
,
40
);
assertEquals
(
"mem: 4 stack: 4 3 2 1 cold: non-resident:"
,
toString
(
test
));
// move middle to front
assertEquals
(
30
,
test
.
get
(
3
).
intValue
());
assertEquals
(
20
,
test
.
get
(
2
).
intValue
());
assertEquals
(
20
,
test
.
peek
(
2
).
intValue
());
// already on (an optimization)
assertEquals
(
20
,
test
.
get
(
2
).
intValue
());
assertEquals
(
10
,
test
.
peek
(
1
).
intValue
());
assertEquals
(
10
,
test
.
get
(
1
).
intValue
());
assertEquals
(
"mem: 4 stack: 1 2 3 4 cold: non-resident:"
,
toString
(
test
));
test
.
put
(
3
,
30
);
assertEquals
(
"mem: 4 stack: 3 1 2 4 cold: non-resident:"
,
toString
(
test
));
// 5 is cold; will make 4 non-resident
test
.
put
(
5
,
50
);
assertEquals
(
"mem: 4 stack: 5 3 1 2 cold: 5 non-resident: 4"
,
toString
(
test
));
assertNull
(
test
.
peek
(
4
));
assertNull
(
test
.
get
(
4
));
assertEquals
(
10
,
test
.
get
(
1
).
intValue
());
assertEquals
(
20
,
test
.
get
(
2
).
intValue
());
assertEquals
(
30
,
test
.
get
(
3
).
intValue
());
assertEquals
(
"mem: 4 stack: 3 2 1 cold: 5 non-resident: 4"
,
toString
(
test
));
assertEquals
(
50
,
test
.
get
(
5
).
intValue
());
assertEquals
(
"mem: 4 stack: 5 3 2 1 cold: 5 non-resident: 4"
,
toString
(
test
));
assertEquals
(
50
,
test
.
get
(
5
).
intValue
());
assertEquals
(
"mem: 4 stack: 5 3 2 cold: 1 non-resident: 4"
,
toString
(
test
));
// remove
assertTrue
(
test
.
remove
(
5
));
assertFalse
(
test
.
remove
(
5
));
assertEquals
(
"mem: 3 stack: 3 2 1 cold: non-resident: 4"
,
toString
(
test
));
assertTrue
(
test
.
remove
(
4
));
assertFalse
(
test
.
remove
(
4
));
assertEquals
(
"mem: 3 stack: 3 2 1 cold: non-resident:"
,
toString
(
test
));
test
.
put
(
4
,
40
);
test
.
put
(
5
,
50
);
assertEquals
(
"mem: 4 stack: 5 4 3 2 cold: 5 non-resident: 1"
,
toString
(
test
));
test
.
get
(
5
);
test
.
get
(
2
);
test
.
get
(
3
);
test
.
get
(
4
);
assertEquals
(
"mem: 4 stack: 4 3 2 5 cold: 2 non-resident: 1"
,
toString
(
test
));
assertTrue
(
test
.
remove
(
5
));
assertEquals
(
"mem: 3 stack: 4 3 2 cold: non-resident: 1"
,
toString
(
test
));
assertTrue
(
test
.
remove
(
2
));
assertTrue
(
test
.
remove
(
1
));
assertEquals
(
"mem: 2 stack: 4 3 cold: non-resident:"
,
toString
(
test
));
test
.
put
(
1
,
10
);
test
.
put
(
2
,
20
);
assertEquals
(
"mem: 4 stack: 2 1 4 3 cold: non-resident:"
,
toString
(
test
));
test
.
get
(
1
);
test
.
get
(
3
);
test
.
get
(
4
);
assertEquals
(
"mem: 4 stack: 4 3 1 2 cold: non-resident:"
,
toString
(
test
));
assertTrue
(
test
.
remove
(
1
));
assertEquals
(
"mem: 3 stack: 4 3 2 cold: non-resident:"
,
toString
(
test
));
test
.
remove
(
2
);
test
.
remove
(
3
);
test
.
remove
(
4
);
// test clear
test
.
clear
();
assertEquals
(
"mem: 0 stack: cold: non-resident:"
,
toString
(
test
));
}
private
void
testLimitHot
()
{
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
100
);
for
(
int
i
=
0
;
i
<
300
;
i
++)
{
test
.
put
(
i
,
10
*
i
);
}
assertEquals
(
199
,
test
.
getSize
());
assertEquals
(
93
,
test
.
getHotSize
());
assertEquals
(
99
,
test
.
getNonResidentSize
());
}
private
void
testLimitNonResident
()
{
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
4
);
for
(
int
i
=
0
;
i
<
20
;
i
++)
{
test
.
put
(
i
,
10
*
i
);
}
assertEquals
(
"mem: 4 stack: 19 18 17 16 3 2 1 cold: 19 non-resident: 18 17 16"
,
toString
(
test
));
}
private
void
testBadHashMethod
()
{
// ensure an 2^n cache size
final
int
size
=
4
;
/**
* A class with a bad hashCode implementation.
*/
class
BadHash
{
int
x
;
BadHash
(
int
x
)
{
this
.
x
=
x
;
}
public
int
hashCode
()
{
return
(
x
&
1
)
*
size
*
2
;
}
public
boolean
equals
(
Object
o
)
{
return
((
BadHash
)
o
).
x
==
x
;
}
public
String
toString
()
{
return
""
+
x
;
}
}
CacheLirs
<
BadHash
,
Integer
>
test
=
CacheLirs
.
newInstance
(
size
*
2
);
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
test
.
put
(
new
BadHash
(
i
),
i
);
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
i
%
3
==
0
)
{
assertTrue
(
test
.
remove
(
new
BadHash
(
i
)));
assertFalse
(
test
.
remove
(
new
BadHash
(
i
)));
}
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
i
%
3
==
0
)
{
assertNull
(
test
.
get
(
new
BadHash
(
i
)));
}
else
{
assertEquals
(
i
,
test
.
get
(
new
BadHash
(
i
)).
intValue
());
}
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
test
.
put
(
new
BadHash
(
i
),
i
);
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
i
%
3
==
0
)
{
assertTrue
(
test
.
remove
(
new
BadHash
(
i
)));
assertFalse
(
test
.
remove
(
new
BadHash
(
i
)));
}
}
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
if
(
i
%
3
==
0
)
{
assertNull
(
test
.
get
(
new
BadHash
(
i
)));
}
else
{
assertEquals
(
i
,
test
.
get
(
new
BadHash
(
i
)).
intValue
());
}
}
}
private
void
testScanResistance
()
{
boolean
log
=
false
;
int
size
=
20
;
// cache size 11 (10 hot, 1 cold)
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
size
/
2
+
1
);
// init the cache with some dummy entries
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
test
.
put
(-
i
,
-
i
*
10
);
}
// init with 0..9, ensure those are hot entries
for
(
int
i
=
0
;
i
<
size
/
2
;
i
++)
{
test
.
put
(
i
,
i
*
10
);
test
.
get
(
i
);
if
(
log
)
{
System
.
out
.
println
(
"get "
+
i
+
" -> "
+
test
);
}
}
// read 0..9, add 10..19 (cold)
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Integer
x
=
test
.
get
(
i
);
Integer
y
=
test
.
peek
(
i
);
if
(
i
<
size
/
2
)
{
assertTrue
(
"i: "
+
i
,
x
!=
null
);
assertTrue
(
"i: "
+
i
,
y
!=
null
);
assertEquals
(
i
*
10
,
x
.
intValue
());
assertEquals
(
i
*
10
,
y
.
intValue
());
}
else
{
assertNull
(
x
);
assertNull
(
y
);
test
.
put
(
i
,
i
*
10
);
// peek should have no effect
assertEquals
(
i
*
10
,
test
.
peek
(
i
).
intValue
());
}
if
(
log
)
{
System
.
out
.
println
(
"get "
+
i
+
" -> "
+
test
);
}
}
// ensure 0..9 are hot, 10..18 are not resident, 19 is cold
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
Integer
x
=
test
.
get
(
i
);
if
(
i
<
size
/
2
||
i
==
size
-
1
)
{
assertTrue
(
"i: "
+
i
,
x
!=
null
);
assertEquals
(
i
*
10
,
x
.
intValue
());
}
else
{
assertNull
(
x
);
}
}
}
private
void
testRandomOperations
()
{
boolean
log
=
false
;
int
size
=
10
;
Random
r
=
new
Random
(
1
);
for
(
int
j
=
0
;
j
<
100
;
j
++)
{
CacheLirs
<
Integer
,
Integer
>
test
=
CacheLirs
.
newInstance
(
size
/
2
);
HashMap
<
Integer
,
Integer
>
good
=
New
.
hashMap
();
for
(
int
i
=
0
;
i
<
10000
;
i
++)
{
int
key
=
r
.
nextInt
(
size
);
int
value
=
r
.
nextInt
();
switch
(
r
.
nextInt
(
3
))
{
case
0
:
if
(
log
)
{
System
.
out
.
println
(
i
+
" put "
+
key
+
" "
+
value
);
}
good
.
put
(
key
,
value
);
test
.
put
(
key
,
value
);
break
;
case
1
:
if
(
log
)
{
System
.
out
.
println
(
i
+
" get "
+
key
);
}
Integer
a
=
good
.
get
(
key
);
Integer
b
=
test
.
get
(
key
);
if
(
a
==
null
)
{
assertNull
(
b
);
}
else
if
(
b
!=
null
)
{
assertEquals
(
a
,
b
);
}
break
;
case
2
:
if
(
log
)
{
System
.
out
.
println
(
i
+
" remove "
+
key
);
}
good
.
remove
(
key
);
test
.
remove
(
key
);
break
;
}
if
(
log
)
{
System
.
out
.
println
(
" -> "
+
toString
(
test
));
}
}
}
}
private
static
<
K
,
V
>
String
toString
(
CacheLirs
<
K
,
V
>
cache
)
{
StringBuilder
buff
=
new
StringBuilder
();
buff
.
append
(
"mem: "
+
cache
.
getUsedMemory
());
buff
.
append
(
" stack:"
);
for
(
K
k
:
cache
.
keys
(
false
,
false
))
{
buff
.
append
(
' '
).
append
(
k
);
}
buff
.
append
(
" cold:"
);
for
(
K
k
:
cache
.
keys
(
true
,
false
))
{
buff
.
append
(
' '
).
append
(
k
);
}
buff
.
append
(
" non-resident:"
);
for
(
K
k
:
cache
.
keys
(
true
,
true
))
{
buff
.
append
(
' '
).
append
(
k
);
}
return
buff
.
toString
();
}
}
h2/src/tools/org/h2/dev/store/btree/CacheLirs.java
浏览文件 @
2d52516f
...
@@ -6,27 +6,51 @@
...
@@ -6,27 +6,51 @@
*/
*/
package
org
.
h2
.
dev
.
store
.
btree
;
package
org
.
h2
.
dev
.
store
.
btree
;
import
org.h2.util.MathUtils
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
/**
* An implementation of the LIRS replacement algorithm from
* A cache.
* Xiaodong Zhang and Song Jiang as described in
* <p>
* http://www.cse.ohio-state.edu/~zhang/lirs-sigmetrics-02.html
* This implementation is not multi-threading save.
* This algorithm is scan resistant.
*
* It is important to use a good hash function for the key (there is no guard against bad hash functions).
* <p>
* An implementation of the LIRS replacement algorithm from Xiaodong Zhang and
* Song Jiang as described in
* http://www.cse.ohio-state.edu/~zhang/lirs-sigmetrics-02.html with a few
* smaller changes: An additional queue for non-resident entries is used, to
* prevent unbound memory usage. The maximum size of this queue is at most the
* size of the rest of the stack. This implementation allows each entry to have
* a distinct memory size. At most 6.25% of the mapped entries are cold.
*
*
* @param <K> the key type
* @param <K> the key type
* @param <V> the value type
* @param <V> the value type
*/
*/
public
class
CacheLirs
<
K
,
V
>
{
public
class
CacheLirs
<
K
,
V
>
{
private
long
maxMemory
=
100
;
private
long
maxMemory
;
private
long
maxMemoryHot
;
private
long
currentMemory
;
private
long
currentMemory
;
private
long
currentMemoryHot
;
private
int
averageMemory
;
private
int
averageMemory
=
1
;
private
int
mapSize
,
stackSize
,
queueSize
,
queue2Size
;
private
Entry
<
K
,
V
>[]
entries
;
private
Entry
<
K
,
V
>[]
entries
;
private
int
mask
;
private
int
mask
;
private
Entry
<
K
,
V
>
head
;
/**
* The stack of recently referenced elements. This includes all hot entries,
* the recently referenced cold entries, and all non-resident cold entries.
*/
private
Entry
<
K
,
V
>
stack
;
/**
* The queue of resident cold entries.
*/
private
Entry
<
K
,
V
>
queue
;
/**
* The queue of non-resident cold entries.
*/
private
Entry
<
K
,
V
>
queue2
;
/**
/**
* Create a new cache.
* Create a new cache.
...
@@ -34,68 +58,86 @@ public class CacheLirs<K, V> {
...
@@ -34,68 +58,86 @@ public class CacheLirs<K, V> {
* @param maxMemory the maximum memory to use
* @param maxMemory the maximum memory to use
* @param averageMemory the average memory usage of an object
* @param averageMemory the average memory usage of an object
*/
*/
p
ublic
CacheLirs
(
long
maxMemory
,
int
averageMemory
)
{
p
rivate
CacheLirs
(
long
maxMemory
,
int
averageMemory
)
{
this
.
maxMemory
=
maxMemory
;
this
.
maxMemory
=
maxMemory
;
this
.
averageMemory
=
averageMemory
;
this
.
averageMemory
=
averageMemory
;
clear
();
clear
();
}
}
/**
* Create a new cache.
*
* @param size the maximum number of elements
*/
public
static
<
K
,
V
>
CacheLirs
<
K
,
V
>
newInstance
(
int
size
)
{
return
new
CacheLirs
<
K
,
V
>(
size
,
1
);
}
/**
/**
* Clear the cache.
* Clear the cache.
*/
*/
public
void
clear
()
{
public
void
clear
()
{
int
len
=
MathUtils
.
convertLongToInt
(
maxMemory
/
averageMemory
);
long
maxLen
=
(
long
)
(
maxMemory
/
averageMemory
/
0.75
);
len
=
MathUtils
.
nextPowerOf2
(
len
);
long
l
=
8
;
maxMemoryHot
=
maxMemory
*
98
/
100
;
while
(
l
<
maxLen
)
{
maxMemoryHot
=
Math
.
min
(
maxMemoryHot
,
maxMemory
-
averageMemory
);
l
+=
l
;
}
int
len
=
(
int
)
Math
.
min
(
1L
<<
31
,
l
);
mask
=
len
-
1
;
mask
=
len
-
1
;
head
=
new
Entry
<
K
,
V
>();
stack
=
new
Entry
<
K
,
V
>();
head
.
stackPrev
=
head
.
stackNext
=
head
;
stack
.
stackPrev
=
stack
.
stackNext
=
stack
;
head
.
queuePrev
=
head
.
queueNext
=
head
;
queue
=
new
Entry
<
K
,
V
>();
queue
.
queuePrev
=
queue
.
queueNext
=
queue
;
queue2
=
new
Entry
<
K
,
V
>();
queue2
.
queuePrev
=
queue2
.
queueNext
=
queue2
;
// first set to null - avoiding out of memory
// first set to null - avoiding out of memory
entries
=
null
;
entries
=
null
;
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
Entry
<
K
,
V
>[]
e
=
new
Entry
[
len
];
Entry
<
K
,
V
>[]
e
=
new
Entry
[
len
];
entries
=
e
;
entries
=
e
;
currentMemory
=
currentMemoryHot
=
0
;
currentMemory
=
0
;
stackSize
=
queueSize
=
queue2Size
=
0
;
}
}
/**
/**
* Get an entry if the entry is cached.
* Get an entry if the entry is cached. This method does not modify the
* internal state.
*
* @param key the key
* @return the value, or null if not found
*/
public
V
peek
(
K
key
)
{
Entry
<
K
,
V
>
e
=
find
(
key
);
return
e
==
null
?
null
:
e
.
value
;
}
/**
* Get an entry if the entry is cached. This method adjusts the internal
* state of the cache, to ensure commonly used entries stay in the cache.
*
*
* @param key the key
* @param key the key
* @return the value, or null if not found
* @return the value, or null if not found
*/
*/
public
V
get
(
K
key
)
{
public
V
get
(
K
key
)
{
Entry
<
K
,
V
>
e
=
find
(
key
);
Entry
<
K
,
V
>
e
=
find
(
key
);
if
(
e
==
null
)
{
if
(
e
==
null
||
e
.
value
==
null
)
{
return
null
;
return
null
;
}
else
if
(
e
.
hot
)
{
}
else
if
(
e
.
isHot
())
{
if
(
e
==
head
.
stackNext
)
{
if
(
e
!=
stack
.
stackNext
)
{
// already the first element
boolean
wasEnd
=
e
==
stack
.
stackPrev
;
}
else
{
removeFromStack
(
e
);
boolean
wasLast
=
e
==
head
.
stackPrev
;
if
(
wasEnd
)
{
e
.
removeFromStack
();
if
(
wasLast
)
{
pruneStack
();
pruneStack
();
}
}
addToStack
(
e
);
addToStack
(
e
);
}
}
}
else
{
}
else
{
if
(
e
.
stackPrev
!=
null
)
{
removeFromQueue
(
e
);
e
.
removeFromStack
();
if
(
e
.
stackNext
!=
null
)
{
e
.
hot
=
true
;
removeFromStack
(
e
);
currentMemoryHot
+=
e
.
memory
;
convertOldestHotToCold
();
e
.
removeFromQueue
();
Entry
<
K
,
V
>
last
=
head
.
stackPrev
;
last
.
removeFromStack
();
last
.
hot
=
false
;
currentMemoryHot
-=
last
.
memory
;
addToQueue
(
last
);
pruneStack
();
}
else
{
}
else
{
e
.
removeFromQueue
();
addToQueue
(
queue
,
e
);
addToQueue
(
e
);
}
}
addToStack
(
e
);
addToStack
(
e
);
}
}
...
@@ -103,52 +145,41 @@ public class CacheLirs<K, V> {
...
@@ -103,52 +145,41 @@ public class CacheLirs<K, V> {
}
}
/**
/**
* Add an entry to the cache. This will assume a memory usage of 1.
* Add an entry to the cache. This method is the same as adding an entry
* with the average memory size.
*
*
* @param key the key
* @param key the key
* @param value the value
* @param value the value
*/
*/
void
put
(
K
key
,
V
value
)
{
public
void
put
(
K
key
,
V
value
)
{
put
(
key
,
value
,
1
);
put
(
key
,
value
,
averageMemory
);
}
}
/**
/**
* Add an entry to the cache.
* Add an entry to the cache. The entry may or may not exist in the cache
* yet. This method will usually mark unknown entries as cold and known
* entries as hot.
*
*
* @param key the key
* @param key the key
* @param value the value
* @param value the value
* @param memory the memory used for the given entry
* @param memory the memory used for the given entry
*/
*/
void
put
(
K
key
,
V
value
,
int
memory
)
{
public
void
put
(
K
key
,
V
value
,
int
memory
)
{
Entry
<
K
,
V
>
e
=
find
(
key
);
if
(
find
(
key
)
!=
null
)
{
if
(
e
!=
null
)
{
if
(
currentMemory
+
memory
>
maxMemoryHot
)
{
if
(
head
!=
head
.
queueNext
)
{
remove
(
head
.
queueNext
.
key
);
}
}
remove
(
key
);
remove
(
key
);
}
}
e
=
new
Entry
<
K
,
V
>();
Entry
<
K
,
V
>
e
=
new
Entry
<
K
,
V
>();
e
.
key
=
key
;
e
.
key
=
key
;
int
hash
=
key
.
hashCode
();
// all pages are hot until the memory limit is reached
e
.
hot
=
currentMemory
+
memory
<=
maxMemoryHot
;
e
.
hashCode
=
hash
;
e
.
value
=
value
;
e
.
value
=
value
;
e
.
memory
=
memory
;
e
.
memory
=
memory
;
int
index
=
hash
&
mask
;
int
index
=
key
.
hashCode
()
&
mask
;
e
.
chained
=
entries
[
index
];
e
.
chained
=
entries
[
index
];
entries
[
index
]
=
e
;
entries
[
index
]
=
e
;
currentMemory
+=
memory
;
currentMemory
+=
memory
;
if
(
e
.
hot
)
{
if
(
currentMemory
>
maxMemory
&&
mapSize
>
0
)
{
currentMemoryHot
+=
memory
;
evict
(
e
);
}
else
{
if
(
currentMemory
>
maxMemory
)
{
removeOld
();
}
addToQueue
(
e
);
}
}
mapSize
++;
addToStack
(
e
);
addToStack
(
e
);
}
}
...
@@ -156,7 +187,7 @@ public class CacheLirs<K, V> {
...
@@ -156,7 +187,7 @@ public class CacheLirs<K, V> {
* Remove an entry.
* Remove an entry.
*
*
* @param key the key
* @param key the key
* @return true if the entry was found
* @return true if the entry was found
(resident or non-resident)
*/
*/
public
boolean
remove
(
K
key
)
{
public
boolean
remove
(
K
key
)
{
int
hash
=
key
.
hashCode
();
int
hash
=
key
.
hashCode
();
...
@@ -165,7 +196,7 @@ public class CacheLirs<K, V> {
...
@@ -165,7 +196,7 @@ public class CacheLirs<K, V> {
if
(
e
==
null
)
{
if
(
e
==
null
)
{
return
false
;
return
false
;
}
}
if
(
e
.
hashCode
==
hash
&&
e
.
key
.
equals
(
key
))
{
if
(
e
.
key
.
equals
(
key
))
{
entries
[
index
]
=
e
.
chained
;
entries
[
index
]
=
e
.
chained
;
}
else
{
}
else
{
Entry
<
K
,
V
>
last
;
Entry
<
K
,
V
>
last
;
...
@@ -175,74 +206,187 @@ public class CacheLirs<K, V> {
...
@@ -175,74 +206,187 @@ public class CacheLirs<K, V> {
if
(
e
==
null
)
{
if
(
e
==
null
)
{
return
false
;
return
false
;
}
}
}
while
(
e
.
hashCode
!=
hash
||
!
e
.
key
.
equals
(
key
));
}
while
(!
e
.
key
.
equals
(
key
));
last
.
chained
=
e
.
chained
;
last
.
chained
=
e
.
chained
;
}
}
mapSize
--;
currentMemory
-=
e
.
memory
;
currentMemory
-=
e
.
memory
;
if
(
e
.
stackNext
!=
null
)
{
if
(
e
.
stackNext
!=
null
)
{
e
.
removeFromStack
();
removeFromStack
(
e
);
}
if
(
e
.
queueNext
!=
null
)
{
e
.
removeFromQueue
();
}
}
if
(
e
.
hot
)
{
if
(
e
.
isHot
())
{
e
=
head
.
queueNext
;
// when removing a hot entry, convert the newest cold entry to hot,
if
(
e
!=
head
)
{
// so that we keep the number of hot entries
e
.
removeFromQueue
();
e
=
queue
.
queueNext
;
e
.
hot
=
true
;
if
(
e
!=
queue
)
{
removeFromQueue
(
e
);
if
(
e
.
stackNext
==
null
)
{
if
(
e
.
stackNext
==
null
)
{
// add to bottom of the stack
addToStackBottom
(
e
);
e
.
stackNext
=
head
;
e
.
stackPrev
=
head
.
stackPrev
;
e
.
stackPrev
.
stackNext
=
e
;
head
.
stackPrev
=
e
;
}
}
}
}
}
else
{
removeFromQueue
(
e
);
}
}
pruneStack
();
return
true
;
return
true
;
}
}
private
void
pruneStack
()
{
private
void
evict
(
Entry
<
K
,
V
>
newCold
)
{
while
(
true
)
{
while
((
queueSize
<<
5
)
<
mapSize
)
{
Entry
<
K
,
V
>
last
=
head
.
stackPrev
;
convertOldestHotToCold
();
if
(
last
==
head
||
last
.
hot
)
{
}
break
;
addToQueue
(
queue
,
newCold
);
while
(
currentMemory
>
maxMemory
)
{
Entry
<
K
,
V
>
e
=
queue
.
queuePrev
;
currentMemory
-=
e
.
memory
;
removeFromQueue
(
e
);
e
.
value
=
null
;
e
.
memory
=
0
;
addToQueue
(
queue2
,
e
);
while
(
queue2Size
+
queue2Size
>
stackSize
)
{
e
=
queue2
.
queuePrev
;
remove
(
e
.
key
);
}
}
last
.
removeFromStack
();
}
}
}
}
private
void
removeOld
()
{
private
void
convertOldestHotToCold
()
{
while
(
currentMemory
>
maxMemory
)
{
Entry
<
K
,
V
>
last
=
stack
.
stackPrev
;
remove
(
head
.
queuePrev
.
key
);
removeFromStack
(
last
);
addToQueue
(
queue
,
last
);
pruneStack
();
}
private
void
pruneStack
()
{
while
(
true
)
{
Entry
<
K
,
V
>
last
=
stack
.
stackPrev
;
if
(
last
==
stack
||
last
.
isHot
())
{
break
;
}
removeFromStack
(
last
);
}
}
}
}
private
Entry
<
K
,
V
>
find
(
K
key
)
{
private
Entry
<
K
,
V
>
find
(
K
key
)
{
int
hash
=
key
.
hashCode
();
int
hash
=
key
.
hashCode
();
Entry
<
K
,
V
>
e
=
entries
[
hash
&
mask
];
Entry
<
K
,
V
>
e
=
entries
[
hash
&
mask
];
while
(
e
!=
null
&&
(
e
.
hashCode
!=
hash
||
!
e
.
key
.
equals
(
key
)
))
{
while
(
e
!=
null
&&
!
e
.
key
.
equals
(
key
))
{
e
=
e
.
chained
;
e
=
e
.
chained
;
}
}
return
e
;
return
e
;
}
}
private
void
addToQueue
(
Entry
<
K
,
V
>
e
)
{
private
void
addToStack
(
Entry
<
K
,
V
>
e
)
{
e
.
queuePrev
=
head
;
e
.
stackPrev
=
stack
;
e
.
queueNext
=
head
.
queueNext
;
e
.
stackNext
=
stack
.
stackNext
;
e
.
stackNext
.
stackPrev
=
e
;
stack
.
stackNext
=
e
;
stackSize
++;
}
private
void
addToStackBottom
(
Entry
<
K
,
V
>
e
)
{
e
.
stackNext
=
stack
;
e
.
stackPrev
=
stack
.
stackPrev
;
e
.
stackPrev
.
stackNext
=
e
;
stack
.
stackPrev
=
e
;
stackSize
++;
}
private
void
removeFromStack
(
Entry
<
K
,
V
>
e
)
{
e
.
stackPrev
.
stackNext
=
e
.
stackNext
;
e
.
stackNext
.
stackPrev
=
e
.
stackPrev
;
e
.
stackPrev
=
e
.
stackNext
=
null
;
stackSize
--;
}
private
void
addToQueue
(
Entry
<
K
,
V
>
q
,
Entry
<
K
,
V
>
e
)
{
e
.
queuePrev
=
q
;
e
.
queueNext
=
q
.
queueNext
;
e
.
queueNext
.
queuePrev
=
e
;
e
.
queueNext
.
queuePrev
=
e
;
head
.
queueNext
=
e
;
q
.
queueNext
=
e
;
if
(
e
.
value
!=
null
)
{
queueSize
++;
}
else
{
queue2Size
++;
}
}
}
private
void
addToStack
(
Entry
<
K
,
V
>
e
)
{
private
void
removeFromQueue
(
Entry
<
K
,
V
>
e
)
{
e
.
stackPrev
=
head
;
e
.
queuePrev
.
queueNext
=
e
.
queueNext
;
e
.
stackNext
=
head
.
stackNext
;
e
.
queueNext
.
queuePrev
=
e
.
queuePrev
;
e
.
stackNext
.
stackPrev
=
e
;
e
.
queuePrev
=
e
.
queueNext
=
null
;
head
.
stackNext
=
e
;
if
(
e
.
value
!=
null
)
{
queueSize
--;
}
else
{
queue2Size
--;
}
}
/**
* Get the number of mapped entries (resident and non-resident).
*
* @return the number of entries
*/
public
int
getSize
()
{
return
mapSize
;
}
/**
* Get the number of hot entries.
*
* @return the number of entries
*/
public
int
getHotSize
()
{
return
mapSize
-
queueSize
-
queue2Size
;
}
/**
* Get the number of non-resident entries.
*
* @return the number of entries
*/
public
int
getNonResidentSize
()
{
return
queue2Size
;
}
}
/**
/**
* A cache entry.
* Get the list of keys for this map. This method allows to view the internal
* state of the cache.
*
* @param cold if true only the keys for the cold entries are returned
* @param nonResident true for non-resident entries
* @return the key set
*/
public
List
<
K
>
keys
(
boolean
cold
,
boolean
nonResident
)
{
ArrayList
<
K
>
s
=
new
ArrayList
<
K
>();
if
(
cold
)
{
Entry
<
K
,
V
>
start
=
nonResident
?
queue2
:
queue
;
for
(
Entry
<
K
,
V
>
e
=
start
.
queueNext
;
e
!=
start
;
e
=
e
.
queueNext
)
{
s
.
add
(
e
.
key
);
}
}
else
{
for
(
Entry
<
K
,
V
>
e
=
stack
.
stackNext
;
e
!=
stack
;
e
=
e
.
stackNext
)
{
s
.
add
(
e
.
key
);
}
}
return
s
;
}
/**
* Get the currently used memory.
*
* @return the used memory
*/
public
long
getUsedMemory
()
{
return
currentMemory
;
}
/**
* A cache entry. Each entry is either hot (low inter-reference recency;
* lir), cold (high inter-reference recency; hir), or non-resident-cold. Hot
* entries are in the stack only. Cold entries are in the queue, and may be
* in the stack. Non-resident-cold entries have their value set to null and
* are in the stack and in the non-resident queue.
*
*
* @param <K> the key type
* @param <K> the key type
* @param <V> the value type
* @param <V> the value type
...
@@ -250,21 +394,13 @@ public class CacheLirs<K, V> {
...
@@ -250,21 +394,13 @@ public class CacheLirs<K, V> {
static
class
Entry
<
K
,
V
>
{
static
class
Entry
<
K
,
V
>
{
K
key
;
K
key
;
V
value
;
V
value
;
int
hashCode
;
int
memory
;
int
memory
;
boolean
hot
;
Entry
<
K
,
V
>
stackPrev
,
stackNext
;
Entry
<
K
,
V
>
stackPrev
,
stackNext
,
queuePrev
,
queueNext
,
chained
;
Entry
<
K
,
V
>
queuePrev
,
queueNext
;
Entry
<
K
,
V
>
chained
;
void
removeFromStack
()
{
stackPrev
.
stackNext
=
stackNext
;
stackNext
.
stackPrev
=
stackPrev
;
stackPrev
=
stackNext
=
null
;
}
void
removeFromQueue
()
{
boolean
isHot
()
{
queuePrev
.
queueNext
=
queueNext
;
return
queueNext
==
null
;
queueNext
.
queuePrev
=
queuePrev
;
queuePrev
=
queueNext
=
null
;
}
}
}
}
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论