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