Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change KryoPool to interface + Builder, make SoftReferences optional. #231

Merged
merged 1 commit into from
Aug 1, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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