diff options
author | Christof Marti | 2004-06-18 19:57:33 +0000 |
---|---|---|
committer | Christof Marti | 2004-06-18 19:57:33 +0000 |
commit | 7a1fadc7459a0717440d72e9bf0e789d20ce50ba (patch) | |
tree | d651d9f747e98bd36ddee36e2406b292f6c2d123 | |
parent | 396e1a550da9397beab7d153e0c3c5d53e2f50ec (diff) | |
download | eclipse.platform.text-20040618_1700.tar.gz eclipse.platform.text-20040618_1700.tar.xz eclipse.platform.text-20040618_1700.zip |
Fix Bug 62662: [performance] OutOfMemoryError commenting StyledTextv20040618_1700
4 files changed, 213 insertions, 21 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.java index c1e00e5d6f1..d05229f33d9 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineChangeHover.java @@ -292,7 +292,7 @@ public class LineChangeHover implements IAnnotationHover, IAnnotationHoverExtens public ILineRange getHoverLineRange(ISourceViewer viewer, int lineNumber) { IDocument document= viewer.getDocument(); if (document != null) { - Point range= computeLineRange(viewer, lineNumber, 0, document.getNumberOfLines()); + Point range= computeLineRange(viewer, lineNumber, 0, Math.max(0, document.getNumberOfLines() - 1)); if (range.x != -1 && range.y != -1) return new LineRange(range.x, range.y - range.x + 1); } diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/DocumentLineDiffer.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/DocumentLineDiffer.java index a6e99ccf975..fb7fd070a84 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/DocumentLineDiffer.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/DocumentLineDiffer.java @@ -49,6 +49,7 @@ import org.eclipse.ui.internal.texteditor.TextEditorPlugin; import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.DocLineComparator; import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.RangeDifference; import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.RangeDifferencer; +import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.LinkedRangeFactory.LowMemoryException; /** * Standard implementation of <code>ILineDiffer</code> as an incremental diff engine. A @@ -67,13 +68,66 @@ import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.Ran */ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnnotationModel { + /** + * Artificial line difference information indicating a change with an empty line as original text. + */ + private static class LineChangeInfo implements ILineDiffInfo { + + private static final String[] ORIGINAL_TEXT= new String[] { "\n" }; //$NON-NLS-1$ + + /* + * @see org.eclipse.jface.text.source.ILineDiffInfo#getRemovedLinesBelow() + */ + public int getRemovedLinesBelow() { + return 0; + } + + /* + * @see org.eclipse.jface.text.source.ILineDiffInfo#getRemovedLinesAbove() + */ + public int getRemovedLinesAbove() { + return 0; + } + + /* + * @see org.eclipse.jface.text.source.ILineDiffInfo#getChangeType() + */ + public int getChangeType() { + return CHANGED; + } + + /* + * @see org.eclipse.jface.text.source.ILineDiffInfo#hasChanges() + */ + public boolean hasChanges() { + return true; + } + + /* + * @see org.eclipse.jface.text.source.ILineDiffInfo#getOriginalText() + */ + public String[] getOriginalText() { + return ORIGINAL_TEXT; + } + } + /** Tells whether this class is in debug mode. */ private static boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.ui.workbench.texteditor/debug/DocumentLineDiffer")); //$NON-NLS-1$//$NON-NLS-2$ + /** Suspended state */ + private static final int SUSPENDED= 0; + /** Initializing state */ + private static final int INITIALIZING= 1; + /** Synchronized state */ + private static final int SYNCHRONIZED= 2; + + /** This differ's state */ + private int fState= SUSPENDED; + /** Artificial line difference information indicating a change with an empty line as original text. */ + private final ILineDiffInfo fLineChangeInfo= new LineChangeInfo(); + /** The provider for the reference document. */ IQuickDiffReferenceProvider fReferenceProvider; - /** Whether this differ is in sync with the model. */ - private boolean fIsSynchronized; /** The number of clients connected to this model. */ private int fOpenConnections; /** The current document being tracked. */ @@ -136,6 +190,9 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno */ public ILineDiffInfo getLineInfo(int line) { + if (isSuspended()) + return fLineChangeInfo; + // try cache first / speeds up linear search RangeDifference last= fLastDifference; if (last != null && last.rightStart() <= line && last.rightEnd() > line) @@ -282,7 +339,7 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno * @return <code>true</code> if we are initialized and in sync with the document. */ private boolean isInitialized() { - return fIsSynchronized; + return fState == SYNCHRONIZED; } /** @@ -291,7 +348,16 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno * @return <code>true</code> if we are initialized and in sync with the document. */ public synchronized boolean isSynchronized() { - return fIsSynchronized; + return fState == SYNCHRONIZED; + } + + /** + * Returns <code>true</code> if the differ is suspended. + * + * @return <code>true</code> if the differ is suspended + */ + private synchronized boolean isSuspended() { + return fState == SUSPENDED; } /** @@ -324,7 +390,7 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno */ synchronized void initialize() { // make new incoming changes go into the queue of stored events, plus signal we can't restore. - fIsSynchronized= false; + fState= INITIALIZING; if (fRightDocument == null) return; @@ -454,7 +520,13 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno // 6: Do Da Diffing DocLineComparator ref= new DocLineComparator(reference, null, false); DocLineComparator act= new DocLineComparator(actual, null, false); - List diffs= RangeDifferencer.findRanges(monitor, ref, act); + List diffs; + try { + diffs= RangeDifferencer.findRanges(monitor, ref, act); + } catch (LowMemoryException e) { + handleLowMemory(e); + return Status.CANCEL_STATUS; + } // 7: Reset the model to the just gotten differences // re-inject stored events to get up to date. @@ -477,7 +549,7 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno if (fStoredEvents.isEmpty()) { // we are done fInitializationJob= null; - fIsSynchronized= true; + fState= SYNCHRONIZED; fLastDifference= null; // inform blocking calls. @@ -500,6 +572,9 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno clearModel(); initialize(); return Status.CANCEL_STATUS; + } catch (LowMemoryException e) { + handleLowMemory(e); + return Status.CANCEL_STATUS; } fireModelChanged(); @@ -634,6 +709,9 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno } catch (ConcurrentModificationException e) { reinitOnError(e); return; + } catch (LowMemoryException e) { + handleLowMemory(e); + return; } // inform listeners about change @@ -672,8 +750,9 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno * Implementation of documentChanged, non synchronized. * * @param event the document event + * @throws LowMemoryException if the differ runs out of memory */ - void handleChanged(DocumentEvent event) throws BadLocationException { + void handleChanged(DocumentEvent event) throws BadLocationException, LowMemoryException { /* * Now, here we have a great example of object oriented programming. */ @@ -1177,7 +1256,7 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno */ private void uninstall() { synchronized (this) { - fIsSynchronized= false; + fState= SUSPENDED; fIgnoreDocumentEvents= true; if (fInitializationJob != null) fInitializationJob.cancel(); @@ -1293,10 +1372,13 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno fRightDocument.removeDocumentListener(this); if (fLeftDocument != null) fLeftDocument.removeDocumentListener(this); + fLeftDocument= null; + fLastDifference= null; + fStoredEvents.clear(); fDifferences.clear(); - fIsSynchronized= false; + fState= SUSPENDED; fireModelChanged(); } @@ -1309,4 +1391,15 @@ public class DocumentLineDiffer implements ILineDiffer, IDocumentListener, IAnno fRightDocument.addDocumentListener(this); initialize(); } + + /** + * Handle low memory situation during diffing. Called from UI and jobs. + * + * @param e the low memory exception + */ + private void handleLowMemory(LowMemoryException e) { + if (DEBUG) + System.err.println("Disabling QuickDiff:\n" + e); //$NON-NLS-1$ + suspend(); + } } diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/LinkedRangeFactory.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/LinkedRangeFactory.java new file mode 100644 index 00000000000..b39acd846d6 --- /dev/null +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/LinkedRangeFactory.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2000, 2004 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer; + +/** + * Memory-monitoring factory for <code>LinkedRangeDifference</code>. + * + * @since 3.0 + */ +public class LinkedRangeFactory { + + /** + * Exception that is thrown after the minimal allowed free memory is reached. + */ + public static class LowMemoryException extends Exception { + + /** + * Initialize without detail message. + */ + public LowMemoryException() { + super(); + } + + /** + * Initialize with the given detail message. + * + * @param message the detail message + */ + public LowMemoryException(String message) { + super(message); + } + } + + /** + * Relative amount of memory that must be free in order to allow the creation of additional instances + */ + private static final double THRESHOLD= 0.1; + + /** + * Number of instantiations after which the amount of free memory is checked + */ + private static final long CHECK_INTERVAL= 10000; + + /** + * Number of instantiations + */ + private long fCount= 0; + + /** + * Create a new linked range difference with the given next range and operation. + * + * @param next the next linked range difference + * @param operation the operation + * @return the new linked range difference + * @throws LowMemoryException + */ + public LinkedRangeDifference newRange(LinkedRangeDifference next, int operation) throws LowMemoryException { + check(); + return new LinkedRangeDifference(next, operation); + } + + /** + * After <code>CHECK_INTERVAL</code> calls check whether at least a fraction of <code>THRESHOLD</code> + * of the maximal available memory is free, otherwise throw an {@link LowMemoryException}. + * + * @throws LowMemoryException + */ + private void check() throws LowMemoryException { + if (++fCount >= CHECK_INTERVAL) { + fCount= 0; + + Runtime runtime= Runtime.getRuntime(); + long maxMemory= runtime.maxMemory(); + long maxFreeMemory= maxMemory - (runtime.totalMemory() - runtime.freeMemory()); + + if (((float) maxFreeMemory) / maxMemory < THRESHOLD) + throw new LowMemoryException(); + } + } +} diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/RangeDifferencer.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/RangeDifferencer.java index 0b08296310c..3dca9700280 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/RangeDifferencer.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/internal/texteditor/quickdiff/compare/rangedifferencer/RangeDifferencer.java @@ -14,6 +14,8 @@ import java.util.*; import org.eclipse.jface.util.Assert; +import org.eclipse.ui.internal.texteditor.quickdiff.compare.rangedifferencer.LinkedRangeFactory.LowMemoryException; + import org.eclipse.core.runtime.IProgressMonitor; /** @@ -56,8 +58,9 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences, or an empty array if no differences were found + * @throws LowMemoryException if the differencer runs out of memory */ - public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) { + public static RangeDifference[] findDifferences(IRangeComparator left, IRangeComparator right) throws LowMemoryException { return findDifferences((IProgressMonitor)null, left, right); } @@ -70,9 +73,10 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences, or an empty array if no differences were found + * @throws LowMemoryException if the differencer runs out of memory * @since 2.0 */ - public static RangeDifference[] findDifferences(IProgressMonitor pm, IRangeComparator left, IRangeComparator right) { + public static RangeDifference[] findDifferences(IProgressMonitor pm, IRangeComparator left, IRangeComparator right) throws LowMemoryException { // assert that both IRangeComparators are of the same class Assert.isTrue(right.getClass().equals(left.getClass())); @@ -107,6 +111,7 @@ public final class RangeDifferencer { return EMPTY_RESULT; //System.out.println("findDifferences: " + maxDiagonal + " " + lower + " " + upper); + LinkedRangeFactory factory= new LinkedRangeFactory(); // for each value of the edit distance for (int d= 1; d <= maxDiagonal; ++d) { // d is the current edit distance @@ -129,13 +134,13 @@ public final class RangeDifferencer { // move down // row= lastDiagonal[k + 1] + 1; - edit= new LinkedRangeDifference(script[k + 1], LinkedRangeDifference.DELETE); + edit= factory.newRange(script[k + 1], LinkedRangeDifference.DELETE); } else { // // move right // row= lastDiagonal[k - 1]; - edit= new LinkedRangeDifference(script[k - 1], LinkedRangeDifference.INSERT); + edit= factory.newRange(script[k - 1], LinkedRangeDifference.INSERT); } col= row + k - origin; edit.fRightStart= row; @@ -180,8 +185,9 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences, or an empty array if no differences were found + * @throws LowMemoryException if the differencer runs out of memory */ - public static RangeDifference[] findDifferences(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { + public static RangeDifference[] findDifferences(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) throws LowMemoryException { return findDifferences(null, ancestor, left, right); } @@ -197,9 +203,10 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences, or an empty array if no differences were found + * @throws LowMemoryException if the differencer runs out of memory * @since 2.0 */ - public static RangeDifference[] findDifferences(IProgressMonitor pm, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { + public static RangeDifference[] findDifferences(IProgressMonitor pm, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) throws LowMemoryException { if (ancestor == null) return findDifferences(pm, left, right); @@ -273,8 +280,9 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences + * @throws LowMemoryException if the differencer runs out of memory */ - public static List findRanges(IRangeComparator left, IRangeComparator right) { + public static List findRanges(IRangeComparator left, IRangeComparator right) throws LowMemoryException { return findRanges((IProgressMonitor)null, left, right); } @@ -287,9 +295,10 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences + * @throws LowMemoryException if the differencer runs out of memory * @since 2.0 */ - public static List findRanges(IProgressMonitor pm, IRangeComparator left, IRangeComparator right) { + public static List findRanges(IProgressMonitor pm, IRangeComparator left, IRangeComparator right) throws LowMemoryException { RangeDifference[] in= findDifferences(pm, left, right); List out= new ArrayList(); @@ -329,8 +338,9 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences + * @throws LowMemoryException if the differencer runs out of memory */ - public static List findRanges(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { + public static List findRanges(IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) throws LowMemoryException { return findRanges(null, ancestor, left, right); } @@ -346,9 +356,10 @@ public final class RangeDifferencer { * @param left the left range comparator * @param right the right range comparator * @return an array of range differences + * @throws LowMemoryException if the differencer runs out of memory * @since 2.0 */ - public static List findRanges(IProgressMonitor pm, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) { + public static List findRanges(IProgressMonitor pm, IRangeComparator ancestor, IRangeComparator left, IRangeComparator right) throws LowMemoryException { if (ancestor == null) return findRanges(pm, left, right); |