diff options
author | Eike Stepper | 2018-05-24 06:54:10 +0000 |
---|---|---|
committer | Eike Stepper | 2018-05-24 06:54:10 +0000 |
commit | bc842e8edda8edaaf40c281760e7791af16075d4 (patch) | |
tree | 1cb3c329151a00ed03fc852bf32c61bb955f8f79 /plugins/org.eclipse.net4j.util | |
parent | 0664b0471e5b12aca1965fabd0b698c57ea42d70 (diff) | |
download | cdo-bc842e8edda8edaaf40c281760e7791af16075d4.tar.gz cdo-bc842e8edda8edaaf40c281760e7791af16075d4.tar.xz cdo-bc842e8edda8edaaf40c281760e7791af16075d4.zip |
[517225] StringCompressor can create huge memory leak
https://bugs.eclipse.org/bugs/show_bug.cgi?id=517225
Diffstat (limited to 'plugins/org.eclipse.net4j.util')
-rw-r--r-- | plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/io/StringCompressor.java | 123 |
1 files changed, 93 insertions, 30 deletions
diff --git a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/io/StringCompressor.java b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/io/StringCompressor.java index e44cfe3bcc..cac5414e89 100644 --- a/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/io/StringCompressor.java +++ b/plugins/org.eclipse.net4j.util/src/org/eclipse/net4j/util/io/StringCompressor.java @@ -11,12 +11,13 @@ package org.eclipse.net4j.util.io; import org.eclipse.net4j.util.CheckUtil; +import org.eclipse.net4j.util.om.OMPlatform; import java.io.IOException; import java.text.MessageFormat; -import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; -import java.util.List; +import java.util.HashSet; import java.util.Map; /** @@ -28,7 +29,7 @@ public class StringCompressor implements StringIO /** * @since 3.0 */ - public static boolean BYPASS = false; + public static boolean BYPASS = OMPlatform.INSTANCE.isProperty("org.eclipse.net4j.util.io.StringCompressor.BYPASS"); private static final int NULL_ID = 0; @@ -56,7 +57,9 @@ public class StringCompressor implements StringIO private Map<Integer, String> idToString = new HashMap<Integer, String>(); - private List<Integer> pendingAcknowledgements = new ArrayList<Integer>(); + private Collection<Integer> pendingAcknowledgements = createAcknowledgementCollection(); + + private long lastAcknowledgementCheck; /** * Creates a StringCompressor instance. @@ -88,8 +91,9 @@ public class StringCompressor implements StringIO } ID id; - List<Integer> acknowledgements = null; + Collection<Integer> acknowledgements = null; boolean stringFollows = false; + synchronized (this) { id = stringToID.get(string); @@ -107,10 +111,11 @@ public class StringCompressor implements StringIO stringFollows = true; } + lastAcknowledgementCheck = System.currentTimeMillis(); if (!pendingAcknowledgements.isEmpty()) { acknowledgements = pendingAcknowledgements; - pendingAcknowledgements = new ArrayList<Integer>(); + pendingAcknowledgements = createAcknowledgementCollection(); } } @@ -125,16 +130,7 @@ public class StringCompressor implements StringIO writeString(out, string); } - if (acknowledgements != null) - { - for (int ack : acknowledgements) - { - writeByte(out, ACK_FOLLOWS); - writeInt(out, ack); - } - } - - writeByte(out, NOTHING_FOLLOWS); + writeAcknowledgements(out, acknowledgements); } else { @@ -156,7 +152,7 @@ public class StringCompressor implements StringIO } String string = null; - List<Integer> acks = null; + Collection<Integer> acks = null; if (id == INFO_FOLLOWS) { id = readInt(in); @@ -178,7 +174,7 @@ public class StringCompressor implements StringIO case ACK_FOLLOWS: if (acks == null) { - acks = new ArrayList<Integer>(); + acks = createAcknowledgementCollection(); } acks.add(readInt(in)); @@ -192,7 +188,8 @@ public class StringCompressor implements StringIO synchronized (this) { - acknowledge(acks); + processAcknowledgements(acks); + if (string != null) { stringToID.put(string, new ID(id)); @@ -212,31 +209,92 @@ public class StringCompressor implements StringIO return string; } - @Override - public String toString() + /** + * @since 3.8 + */ + public Collection<Integer> getPendingAcknowledgements(long timeout) { - return MessageFormat.format("StringCompressor[client={0}]", client); //$NON-NLS-1$ + Collection<Integer> acknowledgements = null; + + synchronized (this) + { + long now = System.currentTimeMillis(); + if (lastAcknowledgementCheck + timeout < now) + { + lastAcknowledgementCheck = now; + if (!pendingAcknowledgements.isEmpty()) + { + acknowledgements = pendingAcknowledgements; + pendingAcknowledgements = createAcknowledgementCollection(); + } + } + } + + return acknowledgements; + } + + /** + * @since 3.8 + */ + public void writeAcknowledgements(ExtendedDataOutput out, Collection<Integer> acknowledgements) throws IOException + { + if (acknowledgements != null) + { + for (int ack : acknowledgements) + { + writeByte(out, ACK_FOLLOWS); + writeInt(out, ack); + } + } + + writeByte(out, NOTHING_FOLLOWS); + } + + /** + * @since 3.8 + */ + public Collection<Integer> readAcknowledgements(ExtendedDataInput in) throws IOException + { + Collection<Integer> acknowledgements = createAcknowledgementCollection(); + while (in.readByte() == ACK_FOLLOWS) + { + acknowledgements.add(in.readInt()); + } + + return acknowledgements; } - private void acknowledge(List<Integer> acks) + /** + * @since 3.8 + */ + public void processAcknowledgements(Collection<Integer> acknowledgements) { - if (acks != null) + if (acknowledgements != null) { - for (int value : acks) + synchronized (this) { - String string = idToString.get(value); - if (string != null) + for (int value : acknowledgements) { - ID id = stringToID.get(string); - if (id != null) + String string = idToString.get(value); + if (string != null) { - id.setAcknowledged(); + ID id = stringToID.get(string); + if (id != null) + { + id.setAcknowledged(); + } } } } } } + @Override + public String toString() + { + return MessageFormat.format("StringCompressor[client={0}]", client); //$NON-NLS-1$ + } + private void writeByte(ExtendedDataOutput out, byte value) throws IOException { if (DEBUG) @@ -375,6 +433,11 @@ public class StringCompressor implements StringIO IOUtil.OUT().println(msg); } + private static Collection<Integer> createAcknowledgementCollection() + { + return new HashSet<Integer>(); + } + /** * @author Eike Stepper */ |