Skip to content

Fix Memory Leak Keeping Players Loaded#706

Open
ByThePowerOfScience wants to merge 1 commit intoCorosauce:1.20from
ByThePowerOfScience:patch-1
Open

Fix Memory Leak Keeping Players Loaded#706
ByThePowerOfScience wants to merge 1 commit intoCorosauce:1.20from
ByThePowerOfScience:patch-1

Conversation

@ByThePowerOfScience
Copy link

@ByThePowerOfScience ByThePowerOfScience commented Dec 2, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments