All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.internal.serialization.impl.bufferpool.BufferPoolThreadLocal Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.internal.serialization.impl.bufferpool;

import com.hazelcast.core.HazelcastInstanceNotActiveException;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.nio.BufferObjectDataOutput;
import com.hazelcast.util.ConcurrentReferenceHashMap;

import java.lang.ref.WeakReference;
import java.util.Map;

import static com.hazelcast.util.ConcurrentReferenceHashMap.ReferenceType.STRONG;
import static com.hazelcast.util.ConcurrentReferenceHashMap.ReferenceType.WEAK;

/**
 * A thread-local for {@link BufferPool}.
 *
 * The BufferPoolThreadLocal is not assigned to a static field, but as a member field of the SerializationService-instance.
 * is unlike the common use of a ThreadLocal where it assigned to a static field. The reason behind this is that the BufferPool
 * instance belongs to a specific SerializationService instance (the buffer pool has buffers tied to a particular
 * SerializationService instance). By creating a ThreadLocal per SerializationService-instance, we obtain 'BufferPool per thread
 * per SerializationService-instance' semantics.
 *
 * 

BufferPool is tied to SerializationService-instance

* The BufferPool contains e.g. {@link BufferObjectDataOutput} instances that have a reference to a specific * SerializationService-instance. So as long as there is a strong reference to the BufferPool, there is a strong reference * to the SerializationService-instance. * *

The problem with regular ThreadLocals

* In the Thread.threadLocal-map only the key (the ThreadLocal) is wrapped in a WeakReference. So when the ThreadLocal-instance * dies, the WeakReference prevents a strong reference to the ThreadLocal and potentially at some point in the future, the map * entry with the null valued WeakReference-key and the strong reference to the the value removed. The problem is we have * no control when this happens and we could end up with a memory leak because there are strong references to e.g. the * SerializationService. * *

BufferPoolThreadLocal wraps BufferPool also in WeakReference

* To prevent this memory leak, the value (the BufferPool) in the Thread.threadLocals-map is also wrapped in a WeakReference * So if there is no strong reference to the BufferPool, the BufferPool is gc'ed. Even though there might by some empty key/value * WeakReferences in the Thread.threadLocal-map. * *

Strong references to the BufferPools

* To prevent the BufferPool wrapped in a WeakReference to be gc'ed as soon as it is created, the BufferPool is also stored * in the BufferPoolThreadLocal in a ConcurrentReferenceHashMap with a weak reference for the key (the thread) and a * strong reference to the value (BufferPool): * * This gives us the following behavior: *
    *
  1. * As soon as the Thread, having the ThreadLocal in its Thread.threadLocals-map, dies, the Entry with the Thread as key * will be removed from the ConcurrentReferenceHashMap at some point. *
  2. *
  3. * As soon as the SerializationService is collected, the BufferPoolThreadLocal is collected. And because of this, there * are no strong references to the BufferPool instances, and they get collected as well. *
  4. *
* So it can be that a Thread in its Thread.threadLocal-map will for some time have an empty weak-reference as key and value. But * there won't be any references to the BufferPool/SerializationService. * *

Performance

* The Performance of using a ThreadLocal in combination with a WeakReference is almost the same as using a ThreadLocal without * WeakReference. There is an extra pointer indirection and some additional pressure on the gc system since it needs to deal with * the WeakReferences, but the number of threads is limited. */ public final class BufferPoolThreadLocal { private final ThreadLocal> threadLocal = new ThreadLocal>(); private final InternalSerializationService serializationService; private final BufferPoolFactory bufferPoolFactory; private final Map strongReferences = new ConcurrentReferenceHashMap(WEAK, STRONG); public BufferPoolThreadLocal(InternalSerializationService serializationService, BufferPoolFactory bufferPoolFactory) { this.serializationService = serializationService; this.bufferPoolFactory = bufferPoolFactory; } public BufferPool get() { WeakReference ref = threadLocal.get(); if (ref == null) { BufferPool pool = bufferPoolFactory.create(serializationService); ref = new WeakReference(pool); strongReferences.put(Thread.currentThread(), pool); threadLocal.set(ref); return pool; } else { BufferPool pool = ref.get(); if (pool == null) { throw new HazelcastInstanceNotActiveException(); } return pool; } } public void clear() { strongReferences.clear(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy