Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
dragonwell8_jdk
提交
d3c5582b
D
dragonwell8_jdk
项目概览
openanolis
/
dragonwell8_jdk
通知
4
Star
2
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
dragonwell8_jdk
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
d3c5582b
编写于
7月 12, 2014
作者:
I
igerasim
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
6904367: (coll) IdentityHashMap is resized before exceeding the expected maximum size
Reviewed-by: plevart, martin
上级
f2fc4f01
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
288 addition
and
67 deletion
+288
-67
src/share/classes/java/util/IdentityHashMap.java
src/share/classes/java/util/IdentityHashMap.java
+62
-67
test/java/util/IdentityHashMap/Capacity.java
test/java/util/IdentityHashMap/Capacity.java
+226
-0
未找到文件。
src/share/classes/java/util/IdentityHashMap.java
浏览文件 @
d3c5582b
/*
/*
* Copyright (c) 2000, 201
3
, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 201
4
, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
*
* This code is free software; you can redistribute it and/or modify it
* This code is free software; you can redistribute it and/or modify it
...
@@ -25,7 +25,6 @@
...
@@ -25,7 +25,6 @@
package
java.util
;
package
java.util
;
import
java.io.*
;
import
java.lang.reflect.Array
;
import
java.lang.reflect.Array
;
import
java.util.function.BiConsumer
;
import
java.util.function.BiConsumer
;
import
java.util.function.BiFunction
;
import
java.util.function.BiFunction
;
...
@@ -74,7 +73,7 @@ import java.util.function.Consumer;
...
@@ -74,7 +73,7 @@ import java.util.function.Consumer;
* maximum size and the number of buckets is unspecified.
* maximum size and the number of buckets is unspecified.
*
*
* <p>If the size of the map (the number of key-value mappings) sufficiently
* <p>If the size of the map (the number of key-value mappings) sufficiently
* exceeds the expected maximum size, the number of buckets is increased
* exceeds the expected maximum size, the number of buckets is increased
.
* Increasing the number of buckets ("rehashing") may be fairly expensive, so
* Increasing the number of buckets ("rehashing") may be fairly expensive, so
* it pays to create identity hash maps with a sufficiently large expected
* it pays to create identity hash maps with a sufficiently large expected
* maximum size. On the other hand, iteration over collection views requires
* maximum size. On the other hand, iteration over collection views requires
...
@@ -160,6 +159,10 @@ public class IdentityHashMap<K,V>
...
@@ -160,6 +159,10 @@ public class IdentityHashMap<K,V>
* The maximum capacity, used if a higher value is implicitly specified
* The maximum capacity, used if a higher value is implicitly specified
* by either of the constructors with arguments.
* by either of the constructors with arguments.
* MUST be a power of two <= 1<<29.
* MUST be a power of two <= 1<<29.
*
* In fact, the map can hold no more than MAXIMUM_CAPACITY-1 items
* because it has to have at least one slot with the key == null
* in order to avoid infinite loops in get(), put(), remove()
*/
*/
private
static
final
int
MAXIMUM_CAPACITY
=
1
<<
29
;
private
static
final
int
MAXIMUM_CAPACITY
=
1
<<
29
;
...
@@ -180,11 +183,6 @@ public class IdentityHashMap<K,V>
...
@@ -180,11 +183,6 @@ public class IdentityHashMap<K,V>
*/
*/
transient
int
modCount
;
transient
int
modCount
;
/**
* The next size value at which to resize (capacity * load factor).
*/
private
transient
int
threshold
;
/**
/**
* Value representing null keys inside tables.
* Value representing null keys inside tables.
*/
*/
...
@@ -229,27 +227,18 @@ public class IdentityHashMap<K,V>
...
@@ -229,27 +227,18 @@ public class IdentityHashMap<K,V>
}
}
/**
/**
* Returns the appropriate capacity for the specified expected maximum
* Returns the appropriate capacity for the given expected maximum size.
* size. Returns the smallest power of two between MINIMUM_CAPACITY
* Returns the smallest power of two between MINIMUM_CAPACITY and
* and MAXIMUM_CAPACITY, inclusive, that is greater than
* MAXIMUM_CAPACITY, inclusive, that is greater than (3 *
* (3 * expectedMaxSize)/2, if such a number exists. Otherwise
* expectedMaxSize)/2, if such a number exists. Otherwise returns
* returns MAXIMUM_CAPACITY. If (3 * expectedMaxSize)/2 is negative, it
* MAXIMUM_CAPACITY.
* is assumed that overflow has occurred, and MAXIMUM_CAPACITY is returned.
*/
*/
private
int
capacity
(
int
expectedMaxSize
)
{
private
static
int
capacity
(
int
expectedMaxSize
)
{
// Compute min capacity for expectedMaxSize given a load factor of 2/3
// assert expectedMaxSize >= 0;
int
minCapacity
=
(
3
*
expectedMaxSize
)/
2
;
return
(
expectedMaxSize
>
MAXIMUM_CAPACITY
/
3
)
?
MAXIMUM_CAPACITY
:
// Compute the appropriate capacity
(
expectedMaxSize
<=
2
*
MINIMUM_CAPACITY
/
3
)
?
MINIMUM_CAPACITY
:
int
result
;
Integer
.
highestOneBit
(
expectedMaxSize
+
(
expectedMaxSize
<<
1
));
if
(
minCapacity
>
MAXIMUM_CAPACITY
||
minCapacity
<
0
)
{
result
=
MAXIMUM_CAPACITY
;
}
else
{
result
=
MINIMUM_CAPACITY
;
while
(
result
<
minCapacity
)
result
<<=
1
;
}
return
result
;
}
}
/**
/**
...
@@ -262,7 +251,6 @@ public class IdentityHashMap<K,V>
...
@@ -262,7 +251,6 @@ public class IdentityHashMap<K,V>
// assert initCapacity >= MINIMUM_CAPACITY;
// assert initCapacity >= MINIMUM_CAPACITY;
// assert initCapacity <= MAXIMUM_CAPACITY;
// assert initCapacity <= MAXIMUM_CAPACITY;
threshold
=
(
initCapacity
*
2
)/
3
;
table
=
new
Object
[
2
*
initCapacity
];
table
=
new
Object
[
2
*
initCapacity
];
}
}
...
@@ -429,52 +417,58 @@ public class IdentityHashMap<K,V>
...
@@ -429,52 +417,58 @@ public class IdentityHashMap<K,V>
* @see #containsKey(Object)
* @see #containsKey(Object)
*/
*/
public
V
put
(
K
key
,
V
value
)
{
public
V
put
(
K
key
,
V
value
)
{
Object
k
=
maskNull
(
key
);
final
Object
k
=
maskNull
(
key
);
Object
[]
tab
=
table
;
int
len
=
tab
.
length
;
int
i
=
hash
(
k
,
len
);
Object
item
;
retryAfterResize:
for
(;;)
{
while
(
(
item
=
tab
[
i
])
!=
null
)
{
final
Object
[]
tab
=
table
;
if
(
item
==
k
)
{
final
int
len
=
tab
.
length
;
@SuppressWarnings
(
"unchecked"
)
int
i
=
hash
(
k
,
len
);
V
oldValue
=
(
V
)
tab
[
i
+
1
];
tab
[
i
+
1
]
=
value
;
for
(
Object
item
;
(
item
=
tab
[
i
])
!=
null
;
return
oldValue
;
i
=
nextKeyIndex
(
i
,
len
))
{
if
(
item
==
k
)
{
@SuppressWarnings
(
"unchecked"
)
V
oldValue
=
(
V
)
tab
[
i
+
1
];
tab
[
i
+
1
]
=
value
;
return
oldValue
;
}
}
}
i
=
nextKeyIndex
(
i
,
len
);
}
modCount
++;
final
int
s
=
size
+
1
;
tab
[
i
]
=
k
;
// Use optimized form of 3 * s.
tab
[
i
+
1
]
=
value
;
// Next capacity is len, 2 * current capacity.
if
(++
size
>=
threshold
)
if
(
s
+
(
s
<<
1
)
>
len
&&
resize
(
len
))
resize
(
len
);
// len == 2 * current capacity.
continue
retryAfterResize
;
return
null
;
modCount
++;
tab
[
i
]
=
k
;
tab
[
i
+
1
]
=
value
;
size
=
s
;
return
null
;
}
}
}
/**
/**
* Resize
the table
to hold given capacity.
* Resize
s the table if necessary
to hold given capacity.
*
*
* @param newCapacity the new capacity, must be a power of two.
* @param newCapacity the new capacity, must be a power of two.
* @return whether a resize did in fact take place
*/
*/
private
void
resize
(
int
newCapacity
)
{
private
boolean
resize
(
int
newCapacity
)
{
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
int
newLength
=
newCapacity
*
2
;
int
newLength
=
newCapacity
*
2
;
Object
[]
oldTable
=
table
;
Object
[]
oldTable
=
table
;
int
oldLength
=
oldTable
.
length
;
int
oldLength
=
oldTable
.
length
;
if
(
oldLength
==
2
*
MAXIMUM_CAPACITY
)
{
// can't expand any further
if
(
oldLength
==
2
*
MAXIMUM_CAPACITY
)
{
// can't expand any further
if
(
threshold
==
MAXIMUM_CAPACITY
-
1
)
if
(
size
==
MAXIMUM_CAPACITY
-
1
)
throw
new
IllegalStateException
(
"Capacity exhausted."
);
throw
new
IllegalStateException
(
"Capacity exhausted."
);
threshold
=
MAXIMUM_CAPACITY
-
1
;
// Gigantic map!
return
false
;
return
;
}
}
if
(
oldLength
>=
newLength
)
if
(
oldLength
>=
newLength
)
return
;
return
false
;
Object
[]
newTable
=
new
Object
[
newLength
];
Object
[]
newTable
=
new
Object
[
newLength
];
threshold
=
newLength
/
3
;
for
(
int
j
=
0
;
j
<
oldLength
;
j
+=
2
)
{
for
(
int
j
=
0
;
j
<
oldLength
;
j
+=
2
)
{
Object
key
=
oldTable
[
j
];
Object
key
=
oldTable
[
j
];
...
@@ -490,6 +484,7 @@ public class IdentityHashMap<K,V>
...
@@ -490,6 +484,7 @@ public class IdentityHashMap<K,V>
}
}
}
}
table
=
newTable
;
table
=
newTable
;
return
true
;
}
}
/**
/**
...
@@ -504,8 +499,8 @@ public class IdentityHashMap<K,V>
...
@@ -504,8 +499,8 @@ public class IdentityHashMap<K,V>
int
n
=
m
.
size
();
int
n
=
m
.
size
();
if
(
n
==
0
)
if
(
n
==
0
)
return
;
return
;
if
(
n
>
threshold
)
// conservatively pre-expand
if
(
n
>
size
)
resize
(
capacity
(
n
));
resize
(
capacity
(
n
));
// conservatively pre-expand
for
(
Entry
<?
extends
K
,
?
extends
V
>
e
:
m
.
entrySet
())
for
(
Entry
<?
extends
K
,
?
extends
V
>
e
:
m
.
entrySet
())
put
(
e
.
getKey
(),
e
.
getValue
());
put
(
e
.
getKey
(),
e
.
getValue
());
...
@@ -542,7 +537,6 @@ public class IdentityHashMap<K,V>
...
@@ -542,7 +537,6 @@ public class IdentityHashMap<K,V>
return
null
;
return
null
;
i
=
nextKeyIndex
(
i
,
len
);
i
=
nextKeyIndex
(
i
,
len
);
}
}
}
}
/**
/**
...
@@ -1266,8 +1260,8 @@ public class IdentityHashMap<K,V>
...
@@ -1266,8 +1260,8 @@ public class IdentityHashMap<K,V>
private
static
final
long
serialVersionUID
=
8188218128353913216L
;
private
static
final
long
serialVersionUID
=
8188218128353913216L
;
/**
/**
* Save the state of the <tt>IdentityHashMap</tt> instance to a stream
* Save
s
the state of the <tt>IdentityHashMap</tt> instance to a stream
* (i.e., serialize it).
* (i.e., serialize
s
it).
*
*
* @serialData The <i>size</i> of the HashMap (the number of key-value
* @serialData The <i>size</i> of the HashMap (the number of key-value
* mappings) (<tt>int</tt>), followed by the key (Object) and
* mappings) (<tt>int</tt>), followed by the key (Object) and
...
@@ -1295,8 +1289,8 @@ public class IdentityHashMap<K,V>
...
@@ -1295,8 +1289,8 @@ public class IdentityHashMap<K,V>
}
}
/**
/**
* Reconstitute the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
* Reconstitute
s
the <tt>IdentityHashMap</tt> instance from a stream (i.e.,
* deserialize it).
* deserialize
s
it).
*/
*/
private
void
readObject
(
java
.
io
.
ObjectInputStream
s
)
private
void
readObject
(
java
.
io
.
ObjectInputStream
s
)
throws
java
.
io
.
IOException
,
ClassNotFoundException
{
throws
java
.
io
.
IOException
,
ClassNotFoundException
{
...
@@ -1305,9 +1299,10 @@ public class IdentityHashMap<K,V>
...
@@ -1305,9 +1299,10 @@ public class IdentityHashMap<K,V>
// Read in size (number of Mappings)
// Read in size (number of Mappings)
int
size
=
s
.
readInt
();
int
size
=
s
.
readInt
();
if
(
size
<
0
)
// Allow for 33% growth (i.e., capacity is >= 2* size()).
throw
new
java
.
io
.
StreamCorruptedException
init
(
capacity
((
size
*
4
)/
3
));
(
"Illegal mappings count: "
+
size
);
init
(
capacity
(
size
));
// Read the keys and values, and put the mappings in the table
// Read the keys and values, and put the mappings in the table
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
for
(
int
i
=
0
;
i
<
size
;
i
++)
{
...
@@ -1324,7 +1319,7 @@ public class IdentityHashMap<K,V>
...
@@ -1324,7 +1319,7 @@ public class IdentityHashMap<K,V>
* update modCount, etc.
* update modCount, etc.
*/
*/
private
void
putForCreate
(
K
key
,
V
value
)
private
void
putForCreate
(
K
key
,
V
value
)
throws
IO
Exception
throws
java
.
io
.
StreamCorrupted
Exception
{
{
Object
k
=
maskNull
(
key
);
Object
k
=
maskNull
(
key
);
Object
[]
tab
=
table
;
Object
[]
tab
=
table
;
...
...
test/java/util/IdentityHashMap/Capacity.java
0 → 100644
浏览文件 @
d3c5582b
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import
java.lang.reflect.Field
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.Random
;
import
org.testng.annotations.DataProvider
;
import
org.testng.annotations.Test
;
import
static
org
.
testng
.
Assert
.*;
/*
* @test
* @bug 6904367
* @summary IdentityHashMap reallocates storage when inserting expected
* number of elements
* @run testng Capacity
*/
@Test
public
class
Capacity
{
static
final
Field
tableField
;
static
final
Random
random
=
new
Random
();
static
final
Object
[][]
sizesData
;
@DataProvider
(
name
=
"sizes"
,
parallel
=
true
)
public
Object
[][]
sizesToTest
()
{
return
sizesData
;
}
static
{
try
{
tableField
=
IdentityHashMap
.
class
.
getDeclaredField
(
"table"
);
tableField
.
setAccessible
(
true
);
}
catch
(
NoSuchFieldException
e
)
{
throw
new
LinkageError
(
"table"
,
e
);
}
ArrayList
<
Object
[]>
sizes
=
new
ArrayList
<>();
for
(
int
size
=
0
;
size
<
200
;
size
++)
sizes
.
add
(
new
Object
[]
{
size
});
// some numbers known to demonstrate bug 6904367
for
(
int
size
:
new
int
[]
{
682
,
683
,
1365
,
2730
,
2731
,
5461
})
sizes
.
add
(
new
Object
[]
{
size
});
// a few more random sizes to try
for
(
int
i
=
0
;
i
!=
128
;
i
++)
sizes
.
add
(
new
Object
[]
{
random
.
nextInt
(
5000
)
});
sizesData
=
sizes
.
toArray
(
new
Object
[
0
][]);
}
static
int
capacity
(
IdentityHashMap
<?,?>
map
)
{
try
{
return
((
Object
[])
tableField
.
get
(
map
)).
length
/
2
;
}
catch
(
Throwable
t
)
{
throw
new
LinkageError
(
"table"
,
t
);
}
}
static
void
assertCapacity
(
IdentityHashMap
<?,?>
map
,
int
expectedCapacity
)
{
assertEquals
(
capacity
(
map
),
expectedCapacity
);
}
static
void
growUsingPut
(
IdentityHashMap
<
Object
,
Object
>
map
,
int
elementsToAdd
)
{
for
(
int
i
=
0
;
i
<
elementsToAdd
;
i
++)
map
.
put
(
new
Object
(),
new
Object
());
}
static
void
growUsingPutAll
(
IdentityHashMap
<
Object
,
Object
>
map
,
int
elementsToAdd
)
{
IdentityHashMap
<
Object
,
Object
>
other
=
new
IdentityHashMap
<>();
growUsingPut
(
other
,
elementsToAdd
);
map
.
putAll
(
other
);
}
static
void
growUsingRepeatedPutAll
(
IdentityHashMap
<
Object
,
Object
>
map
,
int
elementsToAdd
)
{
for
(
int
i
=
0
;
i
<
elementsToAdd
;
i
++)
map
.
putAll
(
Collections
.
singletonMap
(
new
Object
(),
new
Object
()));
}
/**
* Checks that expected number of items can be inserted into
* the map without resizing of the internal storage
*/
@Test
(
dataProvider
=
"sizes"
)
public
void
canInsertExpectedItemsWithoutResizing
(
int
size
)
throws
Throwable
{
// First try growing using put()
IdentityHashMap
<
Object
,
Object
>
m
=
new
IdentityHashMap
<>(
size
);
int
initialCapacity
=
capacity
(
m
);
growUsingPut
(
m
,
size
);
assertCapacity
(
m
,
initialCapacity
);
// Doubling from the expected size will cause exactly one
// resize, except near minimum capacity.
if
(
size
>
1
)
{
growUsingPut
(
m
,
size
);
assertCapacity
(
m
,
2
*
initialCapacity
);
}
// Try again, growing with putAll()
m
=
new
IdentityHashMap
<>(
size
);
initialCapacity
=
capacity
(
m
);
growUsingPutAll
(
m
,
size
);
assertCapacity
(
m
,
initialCapacity
);
// Doubling from the expected size will cause exactly one
// resize, except near minimum capacity.
if
(
size
>
1
)
{
growUsingPutAll
(
m
,
size
);
assertCapacity
(
m
,
2
*
initialCapacity
);
}
}
/**
* Given the expected size, computes such a number N of items that
* inserting (N+1) items will trigger resizing of the internal storage
*/
static
int
threshold
(
int
size
)
throws
Throwable
{
IdentityHashMap
<
Object
,
Object
>
m
=
new
IdentityHashMap
<>(
size
);
int
initialCapacity
=
capacity
(
m
);
while
(
capacity
(
m
)
==
initialCapacity
)
growUsingPut
(
m
,
1
);
return
m
.
size
()
-
1
;
}
/**
* Checks that inserting (threshold+1) item causes resizing
* of the internal storage
*/
@Test
(
dataProvider
=
"sizes"
)
public
void
passingThresholdCausesResize
(
int
size
)
throws
Throwable
{
final
int
threshold
=
threshold
(
size
);
IdentityHashMap
<
Object
,
Object
>
m
=
new
IdentityHashMap
<>(
threshold
);
int
initialCapacity
=
capacity
(
m
);
growUsingPut
(
m
,
threshold
);
assertCapacity
(
m
,
initialCapacity
);
growUsingPut
(
m
,
1
);
assertCapacity
(
m
,
2
*
initialCapacity
);
}
/**
* Checks that 4 methods of requiring capacity lead to the same
* internal capacity, unless sized below default capacity.
*/
@Test
(
dataProvider
=
"sizes"
)
public
void
differentGrowthPatternsResultInSameCapacity
(
int
size
)
throws
Throwable
{
if
(
size
<
21
)
// 21 is default maxExpectedSize
return
;
IdentityHashMap
<
Object
,
Object
>
m
;
m
=
new
IdentityHashMap
<
Object
,
Object
>(
size
);
int
capacity1
=
capacity
(
m
);
m
=
new
IdentityHashMap
<>();
growUsingPut
(
m
,
size
);
int
capacity2
=
capacity
(
m
);
m
=
new
IdentityHashMap
<>();
growUsingPutAll
(
m
,
size
);
int
capacity3
=
capacity
(
m
);
m
=
new
IdentityHashMap
<>();
growUsingRepeatedPutAll
(
m
,
size
);
int
capacity4
=
capacity
(
m
);
if
(
capacity1
!=
capacity2
||
capacity2
!=
capacity3
||
capacity3
!=
capacity4
)
throw
new
AssertionError
(
"Capacities not equal: "
+
capacity1
+
" "
+
capacity2
+
" "
+
capacity3
+
" "
+
capacity4
);
}
public
void
defaultExpectedMaxSizeIs21
()
{
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(),
32
);
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
21
),
32
);
}
public
void
minimumCapacityIs4
()
{
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
0
),
4
);
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
1
),
4
);
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
2
),
4
);
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
3
),
8
);
}
@Test
(
enabled
=
false
)
/** needs too much memory to run normally */
public
void
maximumCapacityIs2ToThe29
()
{
assertCapacity
(
new
IdentityHashMap
<
Long
,
Long
>(
Integer
.
MAX_VALUE
),
1
<<
29
);
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录