Minimize memory footprint for AbstractChannelHandlerContext for handlers that execute in the EventExecutor. (#8786)

Motivation:

We cache the Runnable for some tasks to reduce GC pressure in 4 different fields. This gives overhead in terms of memory usage in all cases, even if we always execute in the EventExecutor (which is the case most of the times).

Modifications:

Move the 4 fields to another class and only have one reference to this in AbstractChannelHandlerContext. This gives a small overhead in the case of execution that is done outside of the EventExecutor but reduce memory footprint in the more likily execution case.

Result:

Less memory used per AbstractChannelHandlerContext in most cases.
This commit is contained in:
Norman Maurer 2019-01-28 19:45:38 +01:00
parent e941cbe27a
commit 185efa5b7c

View File

@ -116,10 +116,7 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
// Lazily instantiated tasks used to trigger events to a handler with different executor.
// There is no need to make this volatile as at worse it will just create a few more instances then needed.
private Runnable invokeChannelReadCompleteTask;
private Runnable invokeReadTask;
private Runnable invokeChannelWritableStateChangedTask;
private Runnable invokeFlushTask;
private Tasks invokeTasks;
private volatile int handlerState = INIT;
@ -486,11 +483,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeChannelReadComplete();
} else {
Runnable task = next.invokeChannelReadCompleteTask;
if (task == null) {
next.invokeChannelReadCompleteTask = task = next::invokeChannelReadComplete;
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeChannelReadCompleteTask);
}
}
@ -517,11 +514,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeChannelWritabilityChanged();
} else {
Runnable task = next.invokeChannelWritableStateChangedTask;
if (task == null) {
next.invokeChannelWritableStateChangedTask = task = next::invokeChannelWritabilityChanged;
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeChannelWritableStateChangedTask);
}
}
@ -781,11 +778,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Runnable task = next.invokeReadTask;
if (task == null) {
next.invokeReadTask = task = next::invokeRead;
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeReadTask);
}
return this;
@ -838,11 +835,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeFlush();
} else {
Runnable task = next.invokeFlushTask;
if (task == null) {
next.invokeFlushTask = task = next::invokeFlush;
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
safeExecute(executor, task, channel().voidPromise(), null);
safeExecute(executor, tasks.invokeFlushTask, channel().voidPromise(), null);
}
return this;
@ -1261,4 +1258,18 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
ctx.invokeFlush();
}
}
private static final class Tasks {
private final Runnable invokeChannelReadCompleteTask;
private final Runnable invokeReadTask;
private final Runnable invokeChannelWritableStateChangedTask;
private final Runnable invokeFlushTask;
Tasks(AbstractChannelHandlerContext next) {
invokeChannelReadCompleteTask = next::invokeChannelReadComplete;
invokeReadTask = next::invokeRead;
invokeChannelWritableStateChangedTask = next::invokeChannelWritabilityChanged;
invokeFlushTask = next::invokeFlush;
}
}
}