memory optimization: reclaim storage for gced references

This commit is contained in:
Alexey Kudravtsev
2011-08-04 15:58:16 +04:00
parent 53f66fda3b
commit 38e950b6c2
5 changed files with 226 additions and 160 deletions
@@ -38,7 +38,10 @@ import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectLocator;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.UserDataHolderEx;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
@@ -104,12 +107,12 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
ApplicationManager.getApplication().addApplicationListener(new ApplicationAdapter() {
@Override
public void beforeWriteActionStart(Object action) {
documentCommitThread.disable(action);
documentCommitThread.disable("Write action started: "+ action);
}
@Override
public void writeActionFinished(Object action) {
documentCommitThread.enable(action);
documentCommitThread.enable("Write action finished: "+action);
}
}, myProject);
documentCommitThread.enable("project open");
@@ -336,13 +339,15 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
}
}
finally {
//myDocumentCommitThread.log("in PDI.finishDoc: ",document, synchronously, success, myUncommittedDocuments);
if (success) {
myUncommittedDocuments.remove(document);
//myDocumentCommitThread.log("in PDI.finishDoc: removed doc",document, synchronously, success, myUncommittedDocuments);
((DocumentImpl)document).normalizeRangeMarkers();
InjectedLanguageUtil.commitAllInjectedDocuments(document, myProject);
}
myIsCommitInProgress = false;
//myDocumentCommitThread.log("in PDI.finishDoc: exit",document, synchronously, success, myUncommittedDocuments);
}
if (success) {
@@ -559,7 +564,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
if (file == null) continue;
if (file.isPhysical() && mySmartPointerManager != null) { // mock tests
SmartPointerManagerImpl.fastenBelts(file, event.getOffset(), null);
mySmartPointerManager.fastenBelts(file, event.getOffset(), null);
}
final TextBlock textBlock = getTextBlock(file);
@@ -598,7 +603,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
for (PsiFile file : files) {
if (file == null || file instanceof PsiFileImpl && ((PsiFileImpl)file).getTreeElement() == null) continue;
if (mySmartPointerManager != null) { // mock tests
SmartPointerManagerImpl.unfastenBelts(file, event.getOffset());
mySmartPointerManager.unfastenBelts(file, event.getOffset());
}
final TextBlock textBlock = getTextBlock(file);
if (textBlock.isLocked()) continue;
@@ -872,7 +872,7 @@ public class FileManagerImpl implements FileManager {
}
if (oldPsiFile != null && oldPsiFile.isPhysical()) {
SmartPointerManagerImpl.fastenBelts(oldPsiFile, 0, null);
((SmartPointerManagerImpl)SmartPointerManager.getInstance(myManager.getProject())).fastenBelts(oldPsiFile, 0, null);
}
ApplicationManager.getApplication().runWriteAction(
new ExternalChangeAction() {
@@ -30,19 +30,18 @@ import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.containers.UnsafeWeakList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class SmartPointerManagerImpl extends SmartPointerManager {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl");
private static final Key<List<WeakReference<SmartPointerEx>>> SMART_POINTERS_IN_PSI_FILE_KEY = Key.create("SMART_POINTERS_IN_DOCUMENT_KEY");
private static final Key<List<SmartPointerEx>> SMART_POINTERS_IN_PSI_FILE_KEY = Key.create("SMART_POINTERS_IN_DOCUMENT_KEY");
private static final Key<Boolean> BELTS_ARE_FASTEN_KEY = Key.create("BELTS_ARE_FASTEN_KEY");
private final Project myProject;
@@ -51,13 +50,13 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
myProject = project;
}
public static void fastenBelts(@NotNull PsiFile file, int offset, @Nullable RangeMarker cachedRangeMarker) {
public void fastenBelts(@NotNull PsiFile file, int offset, @Nullable RangeMarker cachedRangeMarker) {
synchronized (file) {
if (areBeltsFastened(file)) return;
file.putUserData(BELTS_ARE_FASTEN_KEY, Boolean.TRUE);
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
List<SmartPointerEx> pointers = getPointers(file);
if (pointers == null) return;
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject());
Document document = psiDocumentManager.getDocument(file);
@@ -65,13 +64,9 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
((DocumentImpl)document).normalizeRangeMarkers();
}
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
WeakReference<SmartPointerEx> reference = pointers.get(i);
SmartPointerEx pointer = reference.get();
for (SmartPointerEx pointer : pointers) {
if (pointer != null) {
pointer.fastenBelt(offset, cachedRangeMarker);
pointers.set(index++, reference);
}
}
@@ -81,15 +76,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
RangeMarker cachedMarker = getCachedRangeMarkerToInjectedFragment(injectedFile);
fastenBelts(injectedFile, 0, cachedMarker);
}
int size = pointers.size();
for (int i = size - 1; i >= index; i--) {
pointers.remove(i);
}
}
}
private static RangeMarker getCachedRangeMarkerToInjectedFragment(PsiFile injectedFile) {
private static RangeMarker getCachedRangeMarkerToInjectedFragment(@NotNull PsiFile injectedFile) {
PsiElement hostContext = injectedFile.getContext();
RangeMarker cachedMarker = null;
if (hostContext != null) {
@@ -102,12 +92,12 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return cachedMarker;
}
public static void unfastenBelts(@NotNull PsiFile file, int offset) {
public void unfastenBelts(@NotNull PsiFile file, int offset) {
synchronized (file) {
PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject());
file.putUserData(BELTS_ARE_FASTEN_KEY, null);
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
List<SmartPointerEx> pointers = getPointers(file);
if (pointers == null) return;
Document document = psiDocumentManager.getDocument(file);
@@ -115,13 +105,9 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
((DocumentImpl)document).normalizeRangeMarkers();
}
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
WeakReference<SmartPointerEx> reference = pointers.get(i);
SmartPointerEx pointer = reference.get();
for (SmartPointerEx pointer : pointers) {
if (pointer != null) {
pointer.unfastenBelt(offset);
pointers.set(index++, reference);
}
}
@@ -130,15 +116,10 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
if (injectedFile == null) continue;
unfastenBelts(injectedFile, 0);
}
int size = pointers.size();
for (int i = size - 1; i >= index; i--) {
pointers.remove(i);
}
}
}
public static void synchronizePointers(PsiFile file) {
public static void synchronizePointers(@NotNull PsiFile file) {
final Set<Language> languages = file.getViewProvider().getLanguages();
for (Language language : languages) {
final PsiFile f = file.getViewProvider().getPsi(language);
@@ -148,17 +129,13 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
}
private static void _synchronizePointers(final PsiFile file) {
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
private static void _synchronizePointers(@NotNull PsiFile file) {
List<SmartPointerEx> pointers = getPointers(file);
if (pointers == null) return;
int index = 0;
for (int i = 0; i < pointers.size(); i++) {
WeakReference<SmartPointerEx> reference = pointers.get(i);
SmartPointerEx pointer = reference.get();
for (SmartPointerEx pointer : pointers) {
if (pointer != null) {
pointer.documentAndPsiInSync();
pointers.set(index++, reference);
}
}
@@ -168,11 +145,6 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
if (injectedfile == null) continue;
_synchronizePointers(injectedfile);
}
int size = pointers.size();
for (int i = size - 1; i >= index; i--) {
pointers.remove(i);
}
}
private static final Key<Reference<SmartPsiElementPointer>> CACHED_SMART_POINTER_KEY = Key.create("CACHED_SMART_POINTER_KEY");
@@ -197,7 +169,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return pointer;
}
private static <E extends PsiElement> SmartPsiElementPointer<E> getCachedPointer(E element) {
private static <E extends PsiElement> SmartPsiElementPointer<E> getCachedPointer(@NotNull E element) {
Reference<SmartPsiElementPointer> data = element.getUserData(CACHED_SMART_POINTER_KEY);
return data == null ? null : data.get();
}
@@ -214,26 +186,15 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
return pointer;
}
private static <E extends PsiElement> void initPointer(SmartPointerEx<E> pointer, PsiFile containingFile) {
private static <E extends PsiElement> void initPointer(@NotNull SmartPointerEx<E> pointer, PsiFile containingFile) {
if (containingFile == null) return;
synchronized (containingFile) {
//Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(containingFile);
//todo
//if (document != null) {
// //[ven] this is a really NASTY hack; when no smart pointer is kept on UsageInfo then remove this conditional
// if (!(element instanceof PsiFile)) {
// PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject);
// LOG.assertTrue(!documentManager.isUncommited(document) || documentManager.isCommittingDocument(document), "Document for : " +
// containingFile + " is not committed");
// }
//}
List<WeakReference<SmartPointerEx>> pointers = getPointers(containingFile);
List<SmartPointerEx> pointers = getPointers(containingFile);
if (pointers == null) {
pointers = new ArrayList<WeakReference<SmartPointerEx>>();
pointers = new UnsafeWeakList<SmartPointerEx>(); // we synchronise access anyway by containingFile
containingFile.putUserData(SMART_POINTERS_IN_PSI_FILE_KEY, pointers);
}
pointers.add(new WeakReference<SmartPointerEx>(pointer));
pointers.add(pointer);
if (areBeltsFastened(containingFile)) {
pointer.fastenBelt(0, null);
@@ -241,11 +202,11 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
}
}
private static List<WeakReference<SmartPointerEx>> getPointers(@NotNull PsiFile containingFile) {
private static List<SmartPointerEx> getPointers(@NotNull PsiFile containingFile) {
return containingFile.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY);
}
private static boolean areBeltsFastened(final PsiFile file) {
private static boolean areBeltsFastened(@NotNull PsiFile file) {
return file.getUserData(BELTS_ARE_FASTEN_KEY) == Boolean.TRUE;
}
@@ -0,0 +1,168 @@
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.util.containers;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* Implementation of the {@link java.util.List} interface which:
* <ul>
* <li>Stores elements using weak semantics (see {@link java.lang.ref.WeakReference})</li>
* <li>Automatically reclaims storage for garbage collected elements</li>
* <li>Is NOT thread safe</li>
* </ul>
*/
public class UnsafeWeakList<T> extends AbstractList<T> {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.containers.UnsafeWeakList");
protected final WeakReferenceArray<T> myArray;
public UnsafeWeakList() {
this(new WeakReferenceArray<T>());
}
// For testing only
UnsafeWeakList(@NotNull WeakReferenceArray<T> array) {
myArray = array;
}
public T get(int index) {
return myArray.get(index);
}
public boolean add(T element) {
tryReduceCapacity(-1);
myArray.add(element);
return true;
}
@Override
public boolean contains(Object o) {
return super.contains(o);
}
public boolean addIfAbsent(T element) {
tryReduceCapacity(-1);
if (contains(element)) return false;
myArray.add(element);
return true;
}
public void add(int index, T element) {
tryReduceCapacity(-1);
myArray.add(index, element);
}
public T remove(int index) {
tryReduceCapacity(-1);
return myArray.remove(index);
}
public Iterator<T> iterator() {
return new MyIterator();
}
public int size() {
return myArray.size();
}
public List<T> toStrongList() {
List<T> result = new ArrayList<T>(myArray.size());
myArray.toStrongCollection(result);
return result;
}
private int tryReduceCapacity(int trackIndex) {
modCount++;
if (canReduceCapacity()) {
return myArray.reduceCapacity(trackIndex);
}
else {
return probablyCompress(trackIndex);
}
}
private int myCompressCountdown = 10;
private int probablyCompress(int trackIndex) {
myCompressCountdown--;
if (myCompressCountdown > 0) return trackIndex;
int newIndex = myArray.compress(trackIndex);
myCompressCountdown = myArray.size() + 10;
return newIndex;
}
private boolean canReduceCapacity() {
return WeakReferenceArray.MINIMUM_CAPACITY * 2 < myArray.getCapacity() &&
myArray.getCapacity() > myArray.getAliveCount() * 3;
}
protected class MyIterator implements Iterator<T> {
private int myNextIndex = -1;
private int myCurrentIndex = -1;
private T myNextElement = null;
private int myModCount = modCount;
public MyIterator() {
findNext();
}
protected void findNext() {
myNextElement = null;
while (myNextElement == null) {
myNextIndex = myArray.nextValid(myNextIndex);
if (myNextIndex >= myArray.size()) {
myNextIndex = -1;
myNextElement = null;
return;
}
myNextElement = myArray.get(myNextIndex);
}
}
public boolean hasNext() {
return myNextElement != null;
}
public T next() {
if (modCount != myModCount) throw new ConcurrentModificationException();
if (myNextElement == null) throw new NoSuchElementException();
T element = myNextElement;
myCurrentIndex = myNextIndex;
findNext();
return element;
}
public void remove() {
if (myCurrentIndex == -1) throw new IllegalStateException();
myArray.remove(myCurrentIndex);
final int removedIndex = myCurrentIndex;
int newIndex = tryReduceCapacity(myNextIndex);
myCurrentIndex = -1;
myModCount = modCount;
if (!hasNext()) return;
if (newIndex < 0) {
LOG.error(" was: " + myNextIndex +
" got: " + newIndex +
" size: " + myArray.size() +
" current: " + removedIndex);
}
myNextIndex = newIndex;
LOG.assertTrue(myArray.get(myNextIndex) == myNextElement);
}
}
}
@@ -15,35 +15,41 @@
*/
package com.intellij.util.containers;
import com.intellij.openapi.diagnostic.Logger;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class WeakList<T> extends AbstractList<T> {
private static final Logger LOG = Logger.getInstance("#com.intellij.util.containers.WeakList");
private final WeakReferenceArray<T> myArray;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* Implementation of the {@link List} interface which:
* <ul>
* <li>Stores elements using weak semantics (see {@link java.lang.ref.WeakReference})</li>
* <li>Automatically reclaims storage for garbage collected elements</li>
* <li>Is thread safe</li>
* </ul>
*/
public class WeakList<T> extends UnsafeWeakList<T> {
public WeakList() {
this(new WeakReferenceArray<T>());
}
// For testing only
WeakList(WeakReferenceArray<T> array) {
myArray = array;
WeakList(@NotNull WeakReferenceArray<T> array) {
super(array);
}
public T get(int index) {
synchronized (myArray) {
return myArray.get(index);
return super.get(index);
}
}
public boolean add(T element) {
synchronized (myArray) {
tryReduceCapacity(-1);
myArray.add(element);
return super.add(element);
}
return true;
}
@Override
@@ -55,24 +61,19 @@ public class WeakList<T> extends AbstractList<T> {
public boolean addIfAbsent(T element) {
synchronized (myArray) {
tryReduceCapacity(-1);
if (contains(element)) return false;
myArray.add(element);
return super.addIfAbsent(element);
}
return true;
}
public void add(int index, T element) {
synchronized (myArray) {
tryReduceCapacity(-1);
myArray.add(index, element);
super.add(index, element);
}
}
public T remove(int index) {
synchronized (myArray) {
tryReduceCapacity(-1);
return myArray.remove(index);
return super.remove(index);
}
}
@@ -84,7 +85,7 @@ public class WeakList<T> extends AbstractList<T> {
}
public Iterator<T> iterator() {
return new MyIterator();
return new MySyncIterator();
}
public int size() {
@@ -101,85 +102,16 @@ public class WeakList<T> extends AbstractList<T> {
}
}
private int tryReduceCapacity(int trackIndex) {
modCount++;
if (canReduceCapacity()) {
return myArray.reduceCapacity(trackIndex);
}
else {
return propablyCompress(trackIndex);
}
}
private int myCompressCountdown = 10;
private int propablyCompress(int trackIndex) {
myCompressCountdown--;
if (myCompressCountdown > 0) return trackIndex;
int newIndex = myArray.compress(trackIndex);
myCompressCountdown = myArray.size() + 10;
return newIndex;
}
private boolean canReduceCapacity() {
return WeakReferenceArray.MINIMUM_CAPACITY*2 < myArray.getCapacity() &&
myArray.getCapacity() > myArray.getAliveCount()*3;
}
private class MyIterator implements Iterator<T> {
private int myNextIndex = -1;
private int myCurrentIndex = -1;
private T myNextElement = null;
private int myModCount = modCount;
public MyIterator() {
findNext();
}
private void findNext() {
private class MySyncIterator extends MyIterator {
protected void findNext() {
synchronized (myArray) {
myNextElement = null;
while (myNextElement == null) {
myNextIndex = myArray.nextValid(myNextIndex);
if (myNextIndex >= myArray.size()) {
myNextIndex = -1;
myNextElement = null;
return;
}
myNextElement = myArray.get(myNextIndex);
}
super.findNext();
}
}
public boolean hasNext() {
return myNextElement != null;
}
public T next() {
if (modCount != myModCount) throw new ConcurrentModificationException();
if (myNextElement == null) throw new NoSuchElementException();
T element = myNextElement;
myCurrentIndex = myNextIndex;
findNext();
return element;
}
public void remove() {
synchronized (myArray) {
if (myCurrentIndex == -1) throw new IllegalStateException();
myArray.remove(myCurrentIndex);
final int removedIndex = myCurrentIndex;
int newIndex = tryReduceCapacity(myNextIndex);
myCurrentIndex = -1;
myModCount = modCount;
if (!hasNext()) return;
if (newIndex < 0) {
LOG.error(" was: " + myNextIndex +
" got: " + newIndex +
" size: " + myArray.size() +
" current: " + removedIndex);
}
myNextIndex = newIndex;
LOG.assertTrue(myArray.get(myNextIndex) == myNextElement);
super.remove();
}
}
}