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 committed by GitHub
parent cd3254df88
commit 948d4a9ec5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 49 additions and 40 deletions

View File

@ -76,10 +76,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;
@ -379,16 +376,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeChannelReadComplete();
} else {
Runnable task = next.invokeChannelReadCompleteTask;
if (task == null) {
next.invokeChannelReadCompleteTask = task = new Runnable() {
@Override
public void run() {
next.invokeChannelReadComplete();
}
};
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeChannelReadCompleteTask);
}
}
@ -415,16 +407,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeChannelWritabilityChanged();
} else {
Runnable task = next.invokeChannelWritableStateChangedTask;
if (task == null) {
next.invokeChannelWritableStateChangedTask = task = new Runnable() {
@Override
public void run() {
next.invokeChannelWritabilityChanged();
}
};
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeChannelWritableStateChangedTask);
}
}
@ -672,16 +659,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeRead();
} else {
Runnable task = next.invokeReadTask;
if (task == null) {
next.invokeReadTask = task = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
Tasks tasks = next.invokeTasks;
if (tasks == null) {
next.invokeTasks = tasks = new Tasks(next);
}
executor.execute(task);
executor.execute(tasks.invokeReadTask);
}
return this;
@ -734,16 +716,11 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
if (executor.inEventLoop()) {
next.invokeFlush();
} else {
Runnable task = next.invokeFlushTask;
if (task == null) {
next.invokeFlushTask = task = new Runnable() {
@Override
public void run() {
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;
@ -1162,4 +1139,36 @@ abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
ctx.invokeFlush();
}
}
private static final class Tasks {
private final AbstractChannelHandlerContext next;
private final Runnable invokeChannelReadCompleteTask = new Runnable() {
@Override
public void run() {
next.invokeChannelReadComplete();
}
};
private final Runnable invokeReadTask = new Runnable() {
@Override
public void run() {
next.invokeRead();
}
};
private final Runnable invokeChannelWritableStateChangedTask = new Runnable() {
@Override
public void run() {
next.invokeChannelWritabilityChanged();
}
};
private final Runnable invokeFlushTask = new Runnable() {
@Override
public void run() {
next.invokeFlush();
}
};
Tasks(AbstractChannelHandlerContext next) {
this.next = next;
}
}
}