8270416: Enhance construction of Identity maps
Reviewed-by: dfuchs, chegar, rhalade, ahgross, smarks, robm
This commit is contained in:
parent
6b6f829b46
commit
5832a34404
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1994, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1994, 2021, 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
|
||||
@ -1254,7 +1254,7 @@ public class Hashtable<K,V>
|
||||
* Reconstitute the Hashtable from a stream (i.e., deserialize it).
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
readHashtable(s);
|
||||
}
|
||||
@ -1263,14 +1263,16 @@ public class Hashtable<K,V>
|
||||
* Perform deserialization of the Hashtable from an ObjectInputStream.
|
||||
* The Properties class overrides this method.
|
||||
*/
|
||||
void readHashtable(java.io.ObjectInputStream s)
|
||||
void readHashtable(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException {
|
||||
// Read in the threshold and loadFactor
|
||||
s.defaultReadObject();
|
||||
|
||||
// Validate loadFactor (ignore threshold - it will be re-computed)
|
||||
if (loadFactor <= 0 || Float.isNaN(loadFactor))
|
||||
throw new StreamCorruptedException("Illegal Load: " + loadFactor);
|
||||
ObjectInputStream.GetField fields = s.readFields();
|
||||
|
||||
// Read and validate loadFactor (ignore threshold - it will be re-computed)
|
||||
float lf = fields.get("loadFactor", 0.75f);
|
||||
if (lf <= 0 || Float.isNaN(lf))
|
||||
throw new StreamCorruptedException("Illegal load factor: " + lf);
|
||||
lf = Math.min(Math.max(0.25f, lf), 4.0f);
|
||||
|
||||
// Read the original length of the array and number of elements
|
||||
int origlength = s.readInt();
|
||||
@ -1282,13 +1284,13 @@ public class Hashtable<K,V>
|
||||
|
||||
// Clamp original length to be more than elements / loadFactor
|
||||
// (this is the invariant enforced with auto-growth)
|
||||
origlength = Math.max(origlength, (int)(elements / loadFactor) + 1);
|
||||
origlength = Math.max(origlength, (int)(elements / lf) + 1);
|
||||
|
||||
// Compute new length with a bit of room 5% + 3 to grow but
|
||||
// no larger than the clamped original length. Make the length
|
||||
// odd if it's large enough, this helps distribute the entries.
|
||||
// Guard against the length ending up zero, that's not valid.
|
||||
int length = (int)((elements + elements / 20) / loadFactor) + 3;
|
||||
int length = (int)((elements + elements / 20) / lf) + 3;
|
||||
if (length > elements && (length & 1) == 0)
|
||||
length--;
|
||||
length = Math.min(length, origlength);
|
||||
@ -1300,8 +1302,9 @@ public class Hashtable<K,V>
|
||||
// Check Map.Entry[].class since it's the nearest public type to
|
||||
// what we're actually creating.
|
||||
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Map.Entry[].class, length);
|
||||
Hashtable.UnsafeHolder.putLoadFactor(this, lf);
|
||||
table = new Entry<?,?>[length];
|
||||
threshold = (int)Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
|
||||
threshold = (int)Math.min(length * lf, MAX_ARRAY_SIZE + 1);
|
||||
count = 0;
|
||||
|
||||
// Read the number of elements and then all the key/value objects
|
||||
@ -1315,6 +1318,18 @@ public class Hashtable<K,V>
|
||||
}
|
||||
}
|
||||
|
||||
// Support for resetting final field during deserializing
|
||||
private static final class UnsafeHolder {
|
||||
private UnsafeHolder() { throw new InternalError(); }
|
||||
private static final jdk.internal.misc.Unsafe unsafe
|
||||
= jdk.internal.misc.Unsafe.getUnsafe();
|
||||
private static final long LF_OFFSET
|
||||
= unsafe.objectFieldOffset(Hashtable.class, "loadFactor");
|
||||
static void putLoadFactor(Hashtable<?, ?> table, float lf) {
|
||||
unsafe.putFloat(table, LF_OFFSET, lf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The put method used by readObject. This is provided because put
|
||||
* is overridable and should not be called in readObject since the
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2000, 2021, 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
|
||||
@ -25,6 +25,8 @@
|
||||
|
||||
package java.util;
|
||||
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
@ -1267,12 +1269,12 @@ public class IdentityHashMap<K,V>
|
||||
* particular order.
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void writeObject(java.io.ObjectOutputStream s)
|
||||
private void writeObject(ObjectOutputStream s)
|
||||
throws java.io.IOException {
|
||||
// Write out and any hidden stuff
|
||||
// Write out size (number of mappings) and any hidden stuff
|
||||
s.defaultWriteObject();
|
||||
|
||||
// Write out size (number of Mappings)
|
||||
// Write out size again (maintained for backward compatibility)
|
||||
s.writeInt(size);
|
||||
|
||||
// Write out keys and values (alternating)
|
||||
@ -1291,18 +1293,20 @@ public class IdentityHashMap<K,V>
|
||||
* deserializes it).
|
||||
*/
|
||||
@java.io.Serial
|
||||
private void readObject(java.io.ObjectInputStream s)
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws java.io.IOException, ClassNotFoundException {
|
||||
// Read in any hidden stuff
|
||||
s.defaultReadObject();
|
||||
// Size (number of mappings) is written to the stream twice
|
||||
// Read first size value and ignore it
|
||||
s.readFields();
|
||||
|
||||
// Read in size (number of Mappings)
|
||||
// Read second size value, validate and assign to size field
|
||||
int size = s.readInt();
|
||||
if (size < 0)
|
||||
throw new java.io.StreamCorruptedException
|
||||
("Illegal mappings count: " + size);
|
||||
int cap = capacity(size);
|
||||
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap);
|
||||
SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, cap*2);
|
||||
this.size = size;
|
||||
init(cap);
|
||||
|
||||
// Read the keys and values, and put the mappings in the table
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2021, 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
|
||||
@ -100,7 +100,7 @@ public class DeserializedLength {
|
||||
);
|
||||
|
||||
for (int elements : new int[]{10, 50, 500, 5000}) {
|
||||
for (float loadFactor : new float[]{0.15f, 0.5f, 0.75f, 1.0f, 2.5f}) {
|
||||
for (float loadFactor : new float[]{0.25f, 0.5f, 0.75f, 1.0f, 2.5f}) {
|
||||
ok &= testDeserializedLength(elements, loadFactor);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user