JDK dynamic proxy-based WeakCache cache implementation mechanism, jdkweakcache

Source: Internet
Author: User

JDK dynamic proxy-based WeakCache cache implementation mechanism, jdkweakcache

In the previous article, we analyzed how the Proxy class is generated internally. We saw that the Proxy uses the cache mechanism internally, if the proxy class can be found in the cache based on the provided class loader and interface array, the proxy class is directly returned. Otherwise, the ProxyClassFactory factory will be called to generate the proxy class. The cache used here is a level-2 Cache. Its level-1 cache key is generated based on the class loader, And the level-2 Cache key is generated based on the Interface array. For specific internal mechanisms, we directly paste the code for detailed explanation.

// Reference the queue private final ReferenceQueue <K> refQueue = new ReferenceQueue <> (); // underlying implementation of the cache. The key is the first-level cache, and the value is the second-level cache. To support null, the map key type is set to Objectprivate final ConcurrentMap <Object, ConcurrentMap <Object, Supplier <V >>> map = new ConcurrentHashMap <> (); // reverseMap records whether all proxy class generators are available to implement the cache expiration mechanism private final ConcurrentMap <Supplier <V>, Boolean> reverseMap = new ConcurrentHashMap <> (); // The factory that generates the second-level cache key. The input here is the KeyFactoryprivate final BiFunction <K, P,?> SubKeyFactory; // The factory that generates the second-level cache value. The input here is ProxyClassFactoryprivate final BiFunction <K, P, V> valueFactory; // constructor, input the factory that generates the second-level cache key and the factory public WeakCache that generates the second-level cache value (BiFunction <K, P,?> SubKeyFactory, BiFunction <K, P, V> valueFactory) {this. subKeyFactory = Objects. requireNonNull (subKeyFactory); this. valueFactory = Objects. requireNonNull (valueFactory );}

First, let's take a look at the WeakCache member variables and constructors. The internal implementation of the WeakCache cache is achieved through ConcurrentMap, and the member variable map is the underlying implementation of the second-level cache, reverseMap is used to implement the cache expiration mechanism. subKeyFactory is the second-level cache key generation factory passed in through the constructor. The input value here is the KeyFactory of the Proxy class, valueFactory is the second-level cache value generation factory passed in through the constructor. Here the ProxyClassFactory of the Proxy class is passed in. Next, let's take a look at the WeakCache get method.

Public V get (K key, P parameter) {// The interface required here cannot be empty Objects. requireNonNull (parameter); // clear expired cache expungeStaleEntries (); // wrap ClassLoader into CacheKey and use it as the key Object cacheKey = CacheKey of the first-level cache. valueOf (key, refQueue); // obtain the level-2 Cache ConcurrentMap <Object, Supplier <V> valuesMap = map. get (cacheKey); // if the corresponding value if (valuesMap = null) is not obtained according to ClassLoader {// put it in CAS mode, if it does not exist, put it, otherwise, the original value ConcurrentMap <Object, Supplier <V> is returned. OldValuesMap = map. putIfAbsent (cacheKey, valuesMap = new ConcurrentHashMap <> (); // if oldValuesMap has a value, it indicates that the put fails if (oldValuesMap! = Null) {valuesMap = oldValuesMap;} // generate a second-level cache key based on the Interface array implemented by the proxy class, which is divided into key0, key1, key2, keyx Object subKey = Objects. requireNonNull (subKeyFactory. apply (key, parameter); // here, the subKey is used to obtain the second-level cache value Supplier <V> supplier = valuesMap. get (subKey); Factory factory = null; // This loop provides a polling mechanism. If the condition is false, retry until the condition is true while (true) {// if the value obtained through subKey is not empty if (supplier! = Null) {// here supplier may be a Factory or a CacheValue // It is not judged here, verify V value = Supplier in the get method of supplier implementation class. get (); if (value! = Null) {return value ;}}if (factory = null) {// create a Factory instance as the value factory = new Factory (key, parameter, subKey, valuesMap);} if (supplier = null) {// here it indicates that the subKey has no corresponding value, and the factory is placed as the value of the subKey into supplier = valuesMap. putIfAbsent (subKey, factory); if (supplier = null) {// It indicates that the factory is successfully put into the cache supplier = factory;} // otherwise, if another thread modifies the value during this period, the value of the subKey will not be assigned again. Instead, the value may be modified by another thread in the} else {// period, then replace the original value with if (valuesMap. replace (subKey, supplier, factory) {// The factory is successfully replaced with the new value supplier = factory;} else {// The replacement fails. continue to use the original value supplier = valuesMap. get (subKey );}}}}

The WeakCache get method does not use locks for synchronization. How does it implement thread security? Because all its member variables that will be modified use ConcurrentMap, this class is thread-safe. Therefore, it delegates its thread security to ConcurrentMap, And the get method minimizes the synchronous code block as much as possible, which can effectively improve WeakCache performance. We can see that the ClassLoader serves as the key of the first-level cache, so that you can filter it by ClassLoader first, because classes loaded by different ClassLoader are different. Then it uses an interface array to generate the second-level cache key. Here it is optimized. Because most classes implement one or two interfaces, the second-level cache key is divided into key0, key1, key2, keyX. Key0 and key2 indicate that 0 to 2 interfaces are implemented, and keyX indicates that 3 or more interfaces are implemented. In fact, most of them only use key1 and key2. The key generation factory is in the Proxy class, and the key factory is passed in through the WeakCache constructor. Here the second-level cache value is a Factory instance, and the final proxy class value is obtained through the Factory.

Private final class Factory implements Supplier <V> {// a level-1 cache key, which is generated based on ClassLoader; // an array of interfaces implemented by the proxy class private final P parameter; // second-level cache key, which generates private final Object subKey Based on the Interface array; // second-level cache private final ConcurrentMap <Object, Supplier <V> valuesMap; Factory (K key, P parameter, object subKey, ConcurrentMap <Object, Supplier <V> valuesMap) {this. key = key; this. parameter = parameter; this. subK Ey = subKey; this. valuesMap = valuesMap;} @ Override public synchronized V get () {// here, the Supplier is retrieved from the second-level cache again to verify whether it is a Factory Supplier <V> supplier = valuesMap. get (subKey); if (supplier! = This) {// verify whether the supplier is the Factory instance itself here. If not, null is returned to allow the caller to continue polling and retry. // during this period, supplier may be replaced with CacheValue, or because the agent class fails to be generated, the return null is removed from the second-level cache;} V value = null; try {// delegate valueFactory to generate the agent class, the passed ProxyClassFactory is used to generate the proxy class value = Objects. requireNonNull (valueFactory. apply (key, parameter);} finally {// if the agent class fails to be generated, delete the second-level cache if (value = null) {valuesMap. remove (subKey, this) ;}// the assert value can be reached only when the value is not empty.! = Null; // use the weak reference to wrap the generated proxy class CacheValue <V> cacheValue = new CacheValue <> (value); // put the packaged cacheValue into the second-level cache, this operation must be successful. Otherwise, the if (valuesMap. replace (subKey, this, cacheValue) {// After successfully placing the cacheValue into the second-level cache, mark it as reverseMap. put (cacheValue, Boolean. TRUE);} else {throw new AssertionError ("shocould not reach here");} // Finally, return the return value of the proxy class not packaged by weak references ;}}

Let's look at the Factory internal Factory class. We can see that its get method is synchronized using the synchronized keyword. After the get method is implemented, the system first verifies whether the suppiler corresponding to the subKey is the factory itself. If not, null is returned, and the WeakCache get method continues to retry. If it is the factory itself, it will delegate ProxyClassFactory to generate the proxy class. ProxyClassFactory is passed in when WeakCache is constructed. This explains why the internal factory ProxyClassFactory of Proxy is finally called to generate the Proxy class. After a proxy class is generated, it is encapsulated with weak references and put into reverseMap. The original proxy class is returned.

So far, we have revealed in detail the implementation of WeakCache, including its first-level cache and second-level cache implementation principles, as well as the principle of second-level cache key generation, finally, how does it call ProxyClassFactory to generate the proxy class. In the next article, we will go deep into the ProxyGenerator class to see the bytecode generation process of the specific proxy class.

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.