This repository has been archived by the owner on Jun 29, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 102
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduce PoolHolder for ThreadLocal-pooled objects #104
logstash-gelf now uses a PoolHolder instead of static final ThreadLocal instances to hold references to ThreadLocal's and the associated objects. Using an instance to hold ThreadLocal instances breaks the cycle so objects can be collected by the GC if the ClassLoader is no longer in use (i.e. because of a hot-redeploy without restarting the JVM). The object root and the PoolHolder are referenced from PoolingGelfMessageBuilder which is created per log appender/handler instance or as static instance (default setting). Holding pools per instance can increase memory usage. Pooling mode can be controlled via logstash-gelf.message.pooling (Environment variable, System property).
- Loading branch information
Showing
6 changed files
with
267 additions
and
76 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 141 additions & 0 deletions
141
src/main/java/biz/paluch/logging/gelf/intern/PoolHolder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package biz.paluch.logging.gelf.intern; | ||
|
||
import java.io.IOException; | ||
import java.nio.ByteBuffer; | ||
|
||
/** | ||
* Holder for {@link ThreadLocal} pools. | ||
* | ||
* @author Mark Paluch | ||
*/ | ||
abstract class PoolHolder { | ||
|
||
public static PoolHolder noop() { | ||
return NoOpPoolHolder.noop(); | ||
} | ||
|
||
public static PoolHolder threadLocal() { | ||
return new ThreadLocalPoolHolder(); | ||
} | ||
|
||
/** | ||
* @return the {@link OutputAccessor.OutputAccessorPoolHolder}. | ||
*/ | ||
public abstract OutputAccessor.OutputAccessorPoolHolder getOutputAccessorPoolHolder(); | ||
|
||
/** | ||
* @return a pooled {@link ReusableGzipOutputStream} instance. | ||
*/ | ||
public abstract ReusableGzipOutputStream getReusableGzipOutputStream(); | ||
|
||
/** | ||
* @return a pooled {@link ByteBuffer}-array instance. | ||
*/ | ||
public abstract ByteBuffer[] getSingleBuffer(); | ||
|
||
/** | ||
* @return a pooled byte-array instance. | ||
*/ | ||
public abstract byte[] getByteArray(); | ||
|
||
private static class NoOpPoolHolder extends PoolHolder { | ||
|
||
private final static NoOpPoolHolder instance = new NoOpPoolHolder(); | ||
|
||
public static PoolHolder noop() { | ||
return instance; | ||
} | ||
|
||
@Override | ||
public OutputAccessor.OutputAccessorPoolHolder getOutputAccessorPoolHolder() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public ReusableGzipOutputStream getReusableGzipOutputStream() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public ByteBuffer[] getSingleBuffer() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
|
||
@Override | ||
public byte[] getByteArray() { | ||
throw new UnsupportedOperationException(); | ||
} | ||
} | ||
|
||
/** | ||
* {@link PoolHolder} backed by {@link ThreadLocal}. | ||
*/ | ||
private static class ThreadLocalPoolHolder extends PoolHolder { | ||
|
||
private final OutputAccessor.OutputAccessorPoolHolder outputAccessorPoolHolder; | ||
|
||
private final ThreadLocal<ReusableGzipOutputStream> streamPool = new ThreadLocal<ReusableGzipOutputStream>() { | ||
@Override | ||
protected ReusableGzipOutputStream initialValue() { | ||
try { | ||
return new ReusableGzipOutputStream(OutputAccessor.pooledStream(outputAccessorPoolHolder)); | ||
} catch (IOException e) { | ||
throw new IllegalStateException(e); | ||
} | ||
} | ||
}; | ||
|
||
private final ThreadLocal<ByteBuffer[]> singleBufferPool = new ThreadLocal<ByteBuffer[]>() { | ||
@Override | ||
protected ByteBuffer[] initialValue() { | ||
return new ByteBuffer[1]; | ||
} | ||
}; | ||
|
||
private final ThreadLocal<byte[]> byteArrayPool = new ThreadLocal<byte[]>() { | ||
@Override | ||
protected byte[] initialValue() { | ||
return new byte[8192 * 2]; | ||
} | ||
}; | ||
|
||
/** | ||
* Create a new {@link PoolHolder} instance. | ||
*/ | ||
public ThreadLocalPoolHolder() { | ||
this(new OutputAccessor.OutputAccessorPoolHolder()); | ||
} | ||
|
||
private ThreadLocalPoolHolder(OutputAccessor.OutputAccessorPoolHolder outputAccessorPoolHolder) { | ||
this.outputAccessorPoolHolder = outputAccessorPoolHolder; | ||
} | ||
|
||
/** | ||
* @return the {@link OutputAccessor.OutputAccessorPoolHolder}. | ||
*/ | ||
public OutputAccessor.OutputAccessorPoolHolder getOutputAccessorPoolHolder() { | ||
return outputAccessorPoolHolder; | ||
} | ||
|
||
/** | ||
* @return a pooled {@link ReusableGzipOutputStream} instance. | ||
*/ | ||
public ReusableGzipOutputStream getReusableGzipOutputStream() { | ||
return streamPool.get(); | ||
} | ||
|
||
/** | ||
* @return a pooled {@link ByteBuffer}-array instance. | ||
*/ | ||
public ByteBuffer[] getSingleBuffer() { | ||
return singleBufferPool.get(); | ||
} | ||
|
||
/** | ||
* @return a pooled byte-array instance. | ||
*/ | ||
public byte[] getByteArray() { | ||
return byteArrayPool.get(); | ||
} | ||
} | ||
} |
Oops, something went wrong.