Skip to content

Commit

Permalink
Change KryoPool to interface + Builder, make SoftReferences optional.
Browse files Browse the repository at this point in the history
Also removed KryoPool.size() to keep the interface minimal (size would be
an issue e.g. for ThreadLocal).
  • Loading branch information
magro committed Aug 1, 2014
1 parent 0a43a64 commit 38c6815
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 104 deletions.
11 changes: 5 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -552,17 +552,15 @@ Kryo k = kryos.get();
...
```

Alternatively you may want to use the `KryoPool` provided by kryo. The `KryoPool` keeps references to `Kryo` instances
using `SoftReference`s, so that `Kryo` instances will be GC'ed when the JVM starts to run out of memory
Alternatively you may want to use the `KryoPool` provided by kryo. The `KryoPool` allows to keep references to `Kryo` instances
using `SoftReference`s, so that `Kryo` instances can be GC'ed when the JVM starts to run out of memory
(of course you could use `ThreadLocal` with `SoftReference`s as well).

Here's an example that shows how to use the `KryoPool`:

```java
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;
import com.esotericsoftware.kryo.pool.*;

KryoFactory factory = new KryoFactory() {
public Kryo create () {
Expand All @@ -571,7 +569,8 @@ KryoFactory factory = new KryoFactory() {
return kryo;
}
};
KryoPool pool = new KryoPool(factory);
// Build pool with SoftReferences enabled (optional)
KryoPool pool = new KryoPool.Builder(factory).softReferences().build();
Kryo kryo = pool.borrow();
// do s.th. with kryo here, and afterwards release it
pool.release(kryo);
Expand Down
103 changes: 65 additions & 38 deletions src/com/esotericsoftware/kryo/pool/KryoPool.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,13 @@
import com.esotericsoftware.kryo.Kryo;

/**
* A simple, queue based pool for {@link Kryo} instances. Kryo instances
* are cached using {@link SoftReference}s and therefore should not cause
* OOMEs.
* A simple pool interface for {@link Kryo} instances. Use the {@link KryoPool.Builder} to
* construct a pool instance.
*
* Usage:
* <pre>
* import com.esotericsoftware.kryo.Kryo;
* import com.esotericsoftware.kryo.pool.KryoCallback;
* import com.esotericsoftware.kryo.pool.KryoFactory;
* import com.esotericsoftware.kryo.pool.KryoPool;
* import com.esotericsoftware.kryo.pool.*;
*
* KryoFactory factory = new KryoFactory() {
* public Kryo create () {
Expand All @@ -26,7 +23,8 @@
* return kryo;
* }
* };
* KryoPool pool = new KryoPool(factory);
* // Simple pool, you might also activate SoftReferences to fight OOMEs.
* KryoPool pool = new KryoPool.Builder(factory).build();
* Kryo kryo = pool.borrow();
* // do s.th. with kryo here, and afterwards release it
* pool.release(kryo);
Expand All @@ -42,45 +40,74 @@
*
* @author Martin Grotzke
*/
public class KryoPool {

private final Queue<SoftReference<Kryo>> queue;
private final KryoFactory factory;
public interface KryoPool {

public KryoPool(KryoFactory factory) {
this(factory, new ConcurrentLinkedQueue<SoftReference<Kryo>>());
}
/**
* Takes a {@link Kryo} instance from the pool or creates a new one
* (using the factory) if the pool is empty.
*/
Kryo borrow ();

public KryoPool(KryoFactory factory, Queue<SoftReference<Kryo>> queue) {
this.factory = factory;
this.queue = queue;
}
/**
* Returns the given {@link Kryo} instance to the pool.
*/
void release (Kryo kryo);

public int size () {
return queue.size();
}
/**
* Runs the provided {@link KryoCallback} with a {@link Kryo} instance
* from the pool (borrow/release around {@link KryoCallback#execute(Kryo)}).
*/
<T> T run(KryoCallback<T> callback);

/**
* Builder for a {@link KryoPool} instance, constructs a {@link KryoPoolQueueImpl} instance.
*/
public static class Builder {

public Kryo borrow () {
Kryo res;
SoftReference<Kryo> ref;
while((ref = queue.poll()) != null) {
if((res = ref.get()) != null) {
return res;
private final KryoFactory factory;
private Queue<Kryo> queue = new ConcurrentLinkedQueue<Kryo>();
private boolean softReferences;

public Builder(KryoFactory factory) {
if(factory == null) {
throw new IllegalArgumentException("factory must not be null");
}
this.factory = factory;
}
return factory.create();
}

public void release (Kryo kryo) {
queue.offer(new SoftReference(kryo));
}
/**
* Use the given queue for pooling kryo instances (by default a {@link ConcurrentLinkedQueue}
* is used).
*/
public Builder queue(Queue<Kryo> queue) {
if(queue == null) {
throw new IllegalArgumentException("queue must not be null");
}
this.queue = queue;
return this;
}

/**
* Use {@link SoftReference}s for pooled {@link Kryo} instances, so that
* instances may be garbage collected when there's memory demand (by default
* disabled).
*/
public Builder softReferences() {
softReferences = true;
return this;
}

/**
* Build the pool.
*/
public KryoPool build() {
Queue<Kryo> q = softReferences ? new SoftReferenceQueue(queue) : queue;
return new KryoPoolQueueImpl(factory, q);
}

public <T> T run(KryoCallback<T> callback) {
Kryo kryo = borrow();
try {
return callback.execute(kryo);
} finally {
release(kryo);
@Override
public String toString () {
return getClass().getName() + "[queue.class=" + queue.getClass() + ", softReferences=" + softReferences + "]";
}
}

Expand Down
53 changes: 53 additions & 0 deletions src/com/esotericsoftware/kryo/pool/KryoPoolQueueImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.esotericsoftware.kryo.pool;

import java.lang.ref.SoftReference;
import java.util.Queue;

import com.esotericsoftware.kryo.Kryo;

/**
* A simple {@link Queue} based {@link KryoPool} implementation, should be built
* using the KryoPool.Builder.
*
* @author Martin Grotzke
*/
class KryoPoolQueueImpl implements KryoPool {

private final Queue<Kryo> queue;
private final KryoFactory factory;

KryoPoolQueueImpl(KryoFactory factory, Queue<Kryo> queue) {
this.factory = factory;
this.queue = queue;
}

public int size () {
return queue.size();
}

public Kryo borrow () {
Kryo res;
if((res = queue.poll()) != null) {
return res;
}
return factory.create();
}

public void release (Kryo kryo) {
queue.offer(kryo);
}

public <T> T run(KryoCallback<T> callback) {
Kryo kryo = borrow();
try {
return callback.execute(kryo);
} finally {
release(kryo);
}
}

public void clear() {
queue.clear();
}

}
117 changes: 117 additions & 0 deletions src/com/esotericsoftware/kryo/pool/SoftReferenceQueue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.esotericsoftware.kryo.pool;

import java.lang.ref.SoftReference;
import java.util.Collection;
import java.util.Iterator;
import java.util.Queue;

import com.esotericsoftware.kryo.Kryo;

/**
* Internally uses {@link SoftReference}s for queued Kryo instances,
* most importantly adjusts the {@link Queue#poll() poll}
* behavior so that gc'ed Kryo instances are skipped.
* Most other methods are unsupported.
*
* @author Martin Grotzke
*/
class SoftReferenceQueue implements Queue<Kryo> {

private Queue<SoftReference<Kryo>> delegate;

public SoftReferenceQueue (Queue<?> delegate) {
this.delegate = (Queue<SoftReference<Kryo>>)delegate;
}

public Kryo poll () {
Kryo res;
SoftReference<Kryo> ref;
while((ref = delegate.poll()) != null) {
if((res = ref.get()) != null) {
return res;
}
}
return null;
}

public boolean offer (Kryo e) {
return delegate.offer(new SoftReference(e));
}

public boolean add (Kryo e) {
return delegate.add(new SoftReference(e));
}

public int size () {
return delegate.size();
}

public boolean isEmpty () {
return delegate.isEmpty();
}

public boolean contains (Object o) {
return delegate.contains(o);
}

public void clear () {
delegate.clear();
}

public boolean equals (Object o) {
return delegate.equals(o);
}

public int hashCode () {
return delegate.hashCode();
}

@Override
public String toString () {
return getClass().getSimpleName() + super.toString();
}

public Iterator<Kryo> iterator () {
throw new UnsupportedOperationException();
}

public Kryo remove () {
throw new UnsupportedOperationException();
}

public Object[] toArray () {
throw new UnsupportedOperationException();
}

public Kryo element () {
throw new UnsupportedOperationException();
}

public Kryo peek () {
throw new UnsupportedOperationException();
}

public <T> T[] toArray (T[] a) {
throw new UnsupportedOperationException();
}

public boolean remove (Object o) {
throw new UnsupportedOperationException();
}

public boolean containsAll (Collection<?> c) {
throw new UnsupportedOperationException();
}

public boolean addAll (Collection<? extends Kryo> c) {
throw new UnsupportedOperationException();
}

public boolean removeAll (Collection<?> c) {
throw new UnsupportedOperationException();
}

public boolean retainAll (Collection<?> c) {
throw new UnsupportedOperationException();
}
}
Loading

0 comments on commit 38c6815

Please sign in to comment.