weather2 icon indicating copy to clipboard operation
weather2 copied to clipboard

Fix Memory Leak Keeping Players Loaded

Open ByThePowerOfScience opened this issue 1 year ago • 0 comments

Changes StormObject#listEntitiesUnderClouds to use WeakReferences instead of retaining the player entirely.

Alternately, you could use this WeakArrayList wrapper I spent like 30 minutes too long on as your list instead to keep the API the same.

WeakArrayList Thing
import org.jetbrains.annotations.NotNull;

import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.function.UnaryOperator;

public class WeakArrayList<T> implements List<T> {
    @Override
    public int size() {
        return internal.size();
    }
    
    @Override
    public boolean isEmpty() {
        return internal.isEmpty();
    }
    
    @Override
    public boolean contains(Object o) {
        return internal.contains(new WeakReference<>(o));
    }
    
    @NotNull
    @Override
    public Iterator<T> iterator() {
        return new Iterator<T>() {
            private int i = 0;
            
            @Override
            public boolean hasNext() {
                return i < WeakArrayList.this.size();
            }
            
            @Override
            public T next() {
                return WeakArrayList.this.get(i++);
            }
        };
    }
    
    @NotNull
    @Override
    public Object[] toArray() {
        return this.toArray(new Object[0]);
    }
    
    @SuppressWarnings("unchecked")
    @NotNull
    @Override
    public <U> U[] toArray(@NotNull U[] a) {
        WeakReference<T>[] array = internal.toArray(new WeakReference[0]);
        U[] out = (U[]) Array.newInstance(a.getClass().arrayType(), internal.size());
        for (int i = 0; i < array.length; i++) {
            out[i] = (U) array[i].get();
        }
        return out;
    }
    
    public boolean add(T t) {
        return internal.add(new WeakReference<>(t));
    }
    
    @Override
    public boolean remove(Object o) {
        return internal.remove(o);
    }
    
    @Override
    public boolean containsAll(@NotNull Collection<?> c) {
        return internal.containsAll(wrapIncomingCollection(c));
    }
    
    @Override
    public boolean addAll(@NotNull Collection<? extends T> c) {
        return internal.addAll((Collection<? extends WeakReference<T>>) wrapIncomingCollection(c));
    }
    
    @Override
    public boolean addAll(int i, @NotNull Collection<? extends T> c) {
        return internal.addAll(i, (Collection<? extends WeakReference<T>>) wrapIncomingCollection(c));
    }
    
    @Override
    public boolean removeAll(@NotNull Collection<?> c) {
        return internal.removeAll(wrapIncomingCollection(c));
    }
    
    @Override
    public boolean retainAll(@NotNull Collection<?> c) {
        return internal.retainAll(wrapIncomingCollection(c));
    }
    
    public void replaceAll(UnaryOperator<T> operator) {
        
        internal.replaceAll((UnaryOperator<WeakReference<T>>) operator.compose(WeakReference<T>::get).andThen(WeakReference::new));
    }
    
    public void sort(Comparator<? super T> c) {
        Comparator<? super WeakReference<T>> wrapped = (w1, w2) -> c.compare(w1.get(), w2.get());
        internal.sort(wrapped);
    }
    
    @Override
    public void clear() {
        internal.clear();
    }
    
    @Override
    public boolean equals(Object o) {
        return internal.equals(new WeakReference<>(o));
    }
    
    @Override
    public int hashCode() {
        return internal.hashCode();
    }
    
    @Override
    public T get(int index) {
        return internal.get(index).get();
    }
    
    @Override
    public T set(int index, T element) {
        return internal.set(index, new WeakReference<>(element)).get();
    }
    
    @Override
    public void add(int index, T element) {
        internal.add(index, new WeakReference<>(element));
    }
    
    @Override
    public T remove(int index) {
        return internal.remove(index).get();
    }
    
    @Override
    public int indexOf(Object o) {
        return internal.indexOf(o);
    }
    
    @Override
    public int lastIndexOf(Object o) {
        return internal.lastIndexOf(o);
    }
    
    @NotNull
    @Override
    public ListIterator<T> listIterator() {
        return new ListIter(0);
    }
    
    @NotNull
    @Override
    public ListIterator<T> listIterator(int index) {
        return new ListIter(index);
    }
    
    @NotNull
    @Override
    public List<T> subList(int fromIndex, int toIndex) {
        WeakArrayList<T> ret = new WeakArrayList<>();
        for (int i = fromIndex; i < toIndex; i++) {
            ret.add(internal.get(i).get());
        }
        return ret;
    }
    
    private final List<WeakReference<T>> internal = new ArrayList<>();
    
    private static <T> Collection<WeakReference<T>> wrapIncomingCollection(Collection<T> incoming) {
        Collection<WeakReference<T>> mapped = new LinkedList<>();
        for (T o : incoming) {
            mapped.add(new WeakReference<>(o));
        }
        return mapped;
    }
    
    private class ListIter implements ListIterator<T> {
        
        ListIter(int startIdx) {
            this.i = startIdx;
        }
        
        private int i;
        
        @Override
        public boolean hasNext() {
            return i < WeakArrayList.this.size();
        }
        
        @Override
        public T next() {
            return WeakArrayList.this.get(i++);
        }
        
        @Override
        public boolean hasPrevious() {
            return i >= 0;
        }
        
        @Override
        public T previous() {
            return WeakArrayList.this.get(--i);
        }
        
        @Override
        public int nextIndex() {
            return i + 1;
        }
        
        @Override
        public int previousIndex() {
            return i - 1;
        }
        
        @Override
        public void remove() {
            WeakArrayList.this.remove(i--);
        }
        
        @Override
        public void set(T t) {
            WeakArrayList.this.set(i, t);
        }
        
        @Override
        public void add(T t) {
            WeakArrayList.this.add(i, t);
        }
        
    }
}

I know this is going to be auto-closed, but I figured you should be aware of the memory leak. It held a few gigs hostage on my server.

ByThePowerOfScience avatar Dec 02 '24 03:12 ByThePowerOfScience