From cd17dc5f4cbbbafa559194ad8dbfcbc00c5e0252 Mon Sep 17 00:00:00 2001 From: RednedEpic Date: Sat, 30 Nov 2019 13:26:51 -0600 Subject: [PATCH] Use FastUtil over Trove for maps/collections to reduce memory usage This should significantly decrease the amount of RAM being used by Geyser. Previously it was using 300mb - 400mb on average. This has dropped it down to 80mb - 120mb. Ideally I'd like to decrease this value further, but that will require some more work. --- .../org/geysermc/connector/entity/Entity.java | 4 +++- .../network/session/cache/EntityCache.java | 18 ++++++++++-------- .../network/session/cache/WindowCache.java | 4 +++- .../translators/item/ItemTranslator.java | 2 +- .../org/geysermc/connector/utils/Toolbox.java | 8 ++++---- .../connector/world/chunk/BlockStorage.java | 18 +++++++++--------- 6 files changed, 30 insertions(+), 24 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/Entity.java b/connector/src/main/java/org/geysermc/connector/entity/Entity.java index ba9fe4ab..15100f13 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/Entity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/Entity.java @@ -34,6 +34,8 @@ import com.nukkitx.protocol.bedrock.data.EntityDataDictionary; import com.nukkitx.protocol.bedrock.data.EntityFlag; import com.nukkitx.protocol.bedrock.data.EntityFlags; import com.nukkitx.protocol.bedrock.packet.*; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; +import it.unimi.dsi.fastutil.longs.LongSet; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.console.GeyserLogger; @@ -69,7 +71,7 @@ public class Entity { protected boolean valid; - protected Set passengers = new HashSet<>(); + protected LongSet passengers = new LongOpenHashSet(); protected Map attributes = new HashMap<>(); protected EntityDataDictionary metadata = new EntityDataDictionary(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java index 80b96500..fd0bd7c5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/EntityCache.java @@ -25,6 +25,10 @@ package org.geysermc.connector.network.session.cache; +import it.unimi.dsi.fastutil.longs.Long2LongMap; +import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import lombok.Getter; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.PlayerEntity; @@ -41,8 +45,8 @@ public class EntityCache { private GeyserSession session; @Getter - private Map entities = new HashMap<>(); - private Map entityIdTranslations = new HashMap<>(); + private Long2ObjectMap entities = new Long2ObjectOpenHashMap<>(); + private Long2LongMap entityIdTranslations = new Long2LongOpenHashMap(); private Map playerEntities = new HashMap<>(); private Map bossbars = new HashMap<>(); @@ -62,12 +66,10 @@ public class EntityCache { public boolean removeEntity(Entity entity, boolean force) { if (entity != null && entity.isValid() && (force || entity.despawnEntity(session))) { - Long geyserId = entityIdTranslations.remove(entity.getEntityId()); - if (geyserId != null) { - entities.remove(geyserId); - if (entity.is(PlayerEntity.class)) { - playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); - } + long geyserId = entityIdTranslations.remove(entity.getEntityId()); + entities.remove(geyserId); + if (entity.is(PlayerEntity.class)) { + playerEntities.remove(entity.as(PlayerEntity.class).getUuid()); } return true; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java index fc7de614..5d21c55c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WindowCache.java @@ -26,6 +26,8 @@ package org.geysermc.connector.network.session.cache; import com.nukkitx.protocol.bedrock.packet.ModalFormRequestPacket; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import lombok.Getter; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.api.window.FormWindow; @@ -38,7 +40,7 @@ public class WindowCache { private GeyserSession session; @Getter - private Map windows = new HashMap(); + private Int2ObjectMap windows = new Int2ObjectOpenHashMap<>(); public WindowCache(GeyserSession session) { this.session = session; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index b8d56ccf..20421059 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -79,7 +79,7 @@ public class ItemTranslator { } public ItemEntry getItem(ItemData data) { - for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.valueCollection()) { + for (ItemEntry itemEntry : Toolbox.ITEM_ENTRIES.values()) { if (itemEntry.getBedrockId() == data.getId() && itemEntry.getBedrockData() == data.getDamage()) { return itemEntry; } diff --git a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java index 849ecc56..74a1be96 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java +++ b/connector/src/main/java/org/geysermc/connector/utils/Toolbox.java @@ -7,8 +7,8 @@ import com.nukkitx.nbt.tag.CompoundTag; import com.nukkitx.nbt.tag.ListTag; import com.nukkitx.protocol.bedrock.packet.StartGamePacket; -import gnu.trove.map.TIntObjectMap; -import gnu.trove.map.hash.TIntObjectHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.console.GeyserLogger; import org.geysermc.connector.network.translators.block.BlockEntry; @@ -23,8 +23,8 @@ public class Toolbox { public static final Collection ITEMS = new ArrayList<>(); public static ListTag BLOCKS; - public static final TIntObjectMap ITEM_ENTRIES = new TIntObjectHashMap<>(); - public static final TIntObjectMap BLOCK_ENTRIES = new TIntObjectHashMap<>(); + public static final Int2ObjectMap ITEM_ENTRIES = new Int2ObjectOpenHashMap<>(); + public static final Int2ObjectMap BLOCK_ENTRIES = new Int2ObjectOpenHashMap<>(); public static void init() { InputStream stream = GeyserConnector.class.getClassLoader().getResourceAsStream("bedrock/runtime_block_states.dat"); diff --git a/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java index db38d4bb..dbb967d4 100644 --- a/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java +++ b/connector/src/main/java/org/geysermc/connector/world/chunk/BlockStorage.java @@ -1,12 +1,15 @@ package org.geysermc.connector.world.chunk; import com.nukkitx.network.VarInts; -import gnu.trove.list.array.TIntArrayList; import io.netty.buffer.ByteBuf; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import org.geysermc.connector.world.GlobalBlockPalette; import org.geysermc.connector.world.chunk.bitarray.BitArray; import org.geysermc.connector.world.chunk.bitarray.BitArrayVersion; +import java.util.function.IntConsumer; + /** * Adapted from NukkitX: https://github.com/NukkitX/Nukkit */ @@ -14,7 +17,7 @@ public class BlockStorage { private static final int SIZE = 4096; - private final TIntArrayList palette; + private final IntList palette; private BitArray bitArray; public BlockStorage() { @@ -23,11 +26,11 @@ public class BlockStorage { public BlockStorage(BitArrayVersion version) { this.bitArray = version.createPalette(SIZE); - this.palette = new TIntArrayList(16, -1); + this.palette = new IntArrayList(16); this.palette.add(0); // Air is at the start of every palette. } - private BlockStorage(BitArray bitArray, TIntArrayList palette) { + private BlockStorage(BitArray bitArray, IntArrayList palette) { this.palette = palette; this.bitArray = bitArray; } @@ -57,10 +60,7 @@ public class BlockStorage { } VarInts.writeInt(buffer, palette.size()); - palette.forEach(id -> { - VarInts.writeInt(buffer, id); - return true; - }); + palette.forEach((IntConsumer) id -> VarInts.writeInt(buffer, id)); } private void onResize(BitArrayVersion version) { @@ -109,6 +109,6 @@ public class BlockStorage { } public BlockStorage copy() { - return new BlockStorage(this.bitArray.copy(), new TIntArrayList(this.palette)); + return new BlockStorage(this.bitArray.copy(), new IntArrayList(this.palette)); } } \ No newline at end of file