Replace LinkedHashSet by ArrayList to avoid iterators.

Motivation:

In a simple load test that creates and closes several 10k streams per second
I have seen Iterator objects using roughly 1.6% of the total committed heap.

Modifications:

Use an ArrayList instead of a LinkedHashSet to store the connection listeners.
That way we can iterate over the list without creating an iterator every time.

Result:

Zero Iterator allocations due to notifying connection listeners.
This commit is contained in:
Jakob Buchgraber 2015-04-02 20:28:28 -07:00 committed by Norman Maurer
parent ef729e7021
commit 35b9aa9302
2 changed files with 29 additions and 24 deletions

View File

@ -43,7 +43,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -54,7 +54,11 @@ import java.util.Set;
*/
public class DefaultHttp2Connection implements Http2Connection {
private final Set<Listener> listeners = new HashSet<Listener>(4);
/**
* We chose a {@link List} over a {@link Set} to avoid allocating an {@link Iterator} objects when iterating over
* the listeners.
*/
private final List<Listener> listeners = new ArrayList<Listener>(4);
private final IntObjectMap<Http2Stream> streamMap = new IntObjectHashMap<Http2Stream>();
private final ConnectionStream connectionStream = new ConnectionStream();
private final Set<Http2Stream> activeStreams = new LinkedHashSet<Http2Stream>();
@ -161,8 +165,8 @@ public class DefaultHttp2Connection implements Http2Connection {
boolean alreadyNotified = goAwayReceived();
localEndpoint.lastKnownStream(lastKnownStream);
if (!alreadyNotified) {
for (Listener listener : listeners) {
listener.onGoAwayReceived(lastKnownStream, errorCode, debugData);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onGoAwayReceived(lastKnownStream, errorCode, debugData);
}
}
}
@ -177,8 +181,8 @@ public class DefaultHttp2Connection implements Http2Connection {
boolean alreadyNotified = goAwaySent();
remoteEndpoint.lastKnownStream(lastKnownStream);
if (!alreadyNotified) {
for (Listener listener : listeners) {
listener.onGoAwaySent(lastKnownStream, errorCode, debugData);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onGoAwaySent(lastKnownStream, errorCode, debugData);
}
}
}
@ -198,8 +202,8 @@ public class DefaultHttp2Connection implements Http2Connection {
// Remove it from the map and priority tree.
streamMap.remove(stream.id());
for (Listener listener : listeners) {
listener.onStreamRemoved(stream);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onStreamRemoved(stream);
}
}
}
@ -376,8 +380,8 @@ public class DefaultHttp2Connection implements Http2Connection {
createdBy().numActiveStreams++;
// Notify the listeners.
for (Listener listener : listeners) {
listener.onStreamActive(this);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onStreamActive(this);
}
}
return this;
@ -397,8 +401,8 @@ public class DefaultHttp2Connection implements Http2Connection {
createdBy().numActiveStreams--;
// Notify the listeners.
for (Listener listener : listeners) {
listener.onStreamClosed(this);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onStreamClosed(this);
}
} finally {
// Mark this stream for removal.
@ -494,8 +498,8 @@ public class DefaultHttp2Connection implements Http2Connection {
}
private void notifyHalfClosed(Http2Stream stream) {
for (Listener listener : listeners) {
listener.onStreamHalfClosed(stream);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onStreamHalfClosed(stream);
}
}
@ -525,8 +529,8 @@ public class DefaultHttp2Connection implements Http2Connection {
}
final short oldWeight = this.weight;
this.weight = weight;
for (Listener l : listeners) {
l.onWeightChanged(this, oldWeight);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onWeightChanged(this, oldWeight);
}
}
}
@ -706,15 +710,15 @@ public class DefaultHttp2Connection implements Http2Connection {
private void notifyParentChanged(List<ParentChangedEvent> events) {
for (int i = 0; i < events.size(); ++i) {
ParentChangedEvent event = events.get(i);
for (Listener l : listeners) {
event.notifyListener(l);
for (int j = 0; j < listeners.size(); j++) {
event.notifyListener(listeners.get(j));
}
}
}
private void notifyParentChanging(Http2Stream stream, Http2Stream newParent) {
for (Listener l : listeners) {
l.onPriorityTreeParentChanging(stream, newParent);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onPriorityTreeParentChanging(stream, newParent);
}
}
@ -859,8 +863,8 @@ public class DefaultHttp2Connection implements Http2Connection {
connectionStream.takeChild(stream, false, events);
// Notify the listeners of the event.
for (Listener listener : listeners) {
listener.onStreamAdded(stream);
for (int i = 0; i < listeners.size(); i++) {
listeners.get(i).onStreamAdded(stream);
}
notifyParentChanged(events);

View File

@ -229,12 +229,13 @@ public interface Http2Connection {
}
/**
* Adds a listener of stream life-cycle events. Adding the same listener multiple times has no effect.
* Adds a listener of stream life-cycle events.
*/
void addListener(Listener listener);
/**
* Removes a listener of stream life-cycle events.
* Removes a listener of stream life-cycle events. If the same listener was added multiple times
* then only the first occurence gets removed.
*/
void removeListener(Listener listener);