Preface
This is quite idle during this period, so I looked at the jdk source code. A general senior development engineer can improve himself by reading some source code. This article summarizes some "small tips" in the JDK source code and shares them for your reference and learning. I won't say much below, let's take a look at the detailed introduction together.
1 i++ vs i--
Line 985 of String source code, in equals method
while (n--!= 0) { if (v1[i] != v2[i]) return false; i++; }This code is used to judge whether the string is equal, but there is a strange thing that uses i--!=0 to make judgments. Don’t we usually use i++? Why use i--? And the number of cycles is the same. The reason is that there will be one more instruction after compilation:
i-- The operation itself will affect CPSR (current program status register). Common flags for CPSR are N (result is negative), Z (result is 0), C (carry), and O (overflow). i > 0, can be directly judged by the Z flag.
i++ operation will also affect CPSR (current program status register), but only affect O (with overflow) flag, which will not help in the judgment of i < n. Therefore, an additional comparison instruction is needed, which means that one more instruction must be executed for each loop.
Simply put, compared to 0, there will be one less instruction. Therefore, recycle i--, high-end, atmospheric and high-end.
2 member variables vs local variables
JDK source code almost uses a local variable to accept member variables in any method, for example
public int compareTo(String anotherString) { int len1 = value.length; int len2 = anotherString.value.length;Because local variables are initialized in the thread stack of the method, while member variables are initialized in the heap memory, obviously the former is faster, so we try to avoid using member variables directly in the method, but instead use local variables.
3 Deliberately loading into registers && Put time-consuming operations outside the lock
In ConcurrentHashMap, the operation of lock segment is very interesting. It is not a direct lock, but is similar to a spin lock. It repeatedly tries to acquire the lock. During the process of acquiring the lock, it will traverse the linked list, so that the data will be loaded into the register cache first, avoiding convenience in the lock process. At the same time, the operation of generating new objects is also placed outside the lock to avoid time-consuming operations in the lock
final V put(K key, int hash, V value, boolean onlyIfAbsent) { /** Before writing to this segment, you need to obtain the exclusive lock of the segment first. It is not to force lock(), but to try*/ HashEntry<K,V> node = tryLock() ? null : scanAndLockForPut(key, hash, value); scanAndLockForPut() source code
private HashEntry<K,V> scanAndLockForPut(K key, int hash, V value) { HashEntry<K,V> first = entryForHash(this, hash); HashEntry<K,V> e = first; HashEntry<K,V> node = null; int retries = -1; // negative while locating node // Looping to get lock while (!tryLock()) { HashEntry<K,V> f; // to recheck first below if (retries < 0) { if (e == null) { if (node == null) // speculatively create node //The hash bit has no value, create a new object, and no need to go to the lock of the put() method to create a new node = new HashEntry<K,V>(hash, key, value, null); retries = 0; } //The hash position key is the same, degenerating into a spin lock else if (key.equals(e.key)) retries = 0; else // The retries can automatically read the linked list into the cache e = e.next; } // When retries>0, it becomes a spin lock. Of course, if the number of retries exceeds MAX_SCAN_RETRIES (single core 1 multi-core 64), then don't snatch it, enter the blocking queue and wait for the lock // lock() is the blocking method until it returns after obtaining the lock, otherwise it will hang else if (++retries > MAX_SCAN_RETRIES) { lock(); break; } else if ((retries & 1) == 0 && // There is a big problem at this time, that is, new elements enter the linked list and become a new header // So the strategy here is that it is equivalent to going through the scanAndLockForPut method again (f = entryForHash(this, hash)) != first) { e = first = f; // re-traverse if entry changed retries = -1; } } return node;} 4 You can use it first to determine the object equality ==
When judging whether the objects are equal, you can use == first, because == directly compare the addresses, which is very fast, while equals will compare the most object values, which is relatively slow. So if possible, you can use a==b || a.equals(b) to compare whether the objects are equal.
5 About transient
Transient is used to prevent serialization, but the internal array in HashMap source code is defined as transient.
/** * The table, resized as necessary. Length MUST Always be a power of two. */ transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
Then won’t the key-value pairs inside cannot be serialized? Isn’t it impossible to transmit using hashmap on the network? In fact, it is not.
Effective Java 2nd, Item75, Joshua mentioned:
For example, consider the case of a hash table. The physical
representation is a sequence of hash buckets containing key-value
entries. The bucket that an entry resides in is a function of the hash
code of its key, which is not, in general, guaranteed to be the same
from JVM implementation to JVM implementation. In fact, it isn't even
guaranteed to be the same from run to run. Therefore, accepting the
default serialized form for a hash table would constitute a serious
bug. Serializing and deserializing the hash table could yield an
object whose invariants were seriously corrupt.
How to understand? Take a look at HashMap.get()/put() to know that reading and writing Map is based on Object.hashcode() to determine which bucket to read/write from. Object.hashcode() is a native method, which may be different in different JVMs.
For example, save an entry to HashMap, the key is the string "STRING". In the first java program, the hashcode() of "STRING" is 1, and the bucket number 1 is stored; in the second java program, the hashcode() of "STRING" may be 2, and the bucket number 2 is stored. If the default serialization is used (Entry[] table does not require transient), then after this HashMap imports the second java program through serialization from the first java program, its memory distribution is the same, which is wrong.
For example, if you save a key-value pair entry to HashMap, key="Fang Laosi", in the first java program, the hashcode() of "Fang Laosi" is 1, and the table[1] is stored. OK, now it is passed to another JVM program, the hashcode() of "Fang Laosi" may be 2, so you go to table[2] to get it, and the result does not exist.
HashMap's current readObject and writeObject are used to output/input content and regenerate HashMap.
6 Don't use char
char is encoded in utf-16 in Java, and is 2 bytes, and 2 bytes cannot represent all characters. The 2 bytes are called BMP, and the other is called high surrogate and low surrogate to form a 4-byte character represented by scribing. For example, indexOf in String source code:
// Here is an int to accept a char to facilitate judgment of the range public int indexOf(int ch, int fromIndex) { final int max = value.length; if (fromIndex < 0) { fromIndex = 0; } else if (fromIndex >= max) { // Note: fromIndex might be near -1>>>1. return -1; } // In the Bmp range if (ch < Character.MIN_SUPPLEMENTARY_CODE_POINT) { // handle most cases here (ch is a BMP code point or a // negative value (invalid code point)) final char[] value = this.value; for (int i = fromIndex; i < max; i++) { if (value[i] == ch) { return i; } } return -1; } else { //Otherwise, go to the four-byte judgment method return indexOfSupplementary(ch, fromIndex); } }Therefore, Java's char can only represent the bmp partial characters in utf16. For CJK (China, Japan and South Korea unified ideogram) partial extended character sets, they cannot be expressed.
For example, char cannot be represented except for the Ext-A part in the figure below.
In addition, there is another saying that you should use char. Don’t use String for the password. String is a constant (that is, it cannot be changed after creation), and will be saved in the constant pool. If other processes can dump the memory of the process, the password will be leaked as the constant pool is dumped, and char[] can write other information to change, which means that it will reduce the risk of leaking the password.
But I personally think you can dump memory. Can a char be able to prevent it? Unless String is not recycled in the constant pool and is read directly from the constant pool by other threads, it is probably very rare.
Summarize
The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support to Wulin.com.