8270416: Enhance construction of Identity maps

Reviewed-by: dfuchs, chegar, rhalade, ahgross, smarks, robm
This commit is contained in:
Julia Boes 2021-08-25 11:41:26 +00:00 committed by Henry Jen
parent 6b6f829b46
commit 5832a34404
3 changed files with 41 additions and 22 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
}
}