Solved Memory leak

Discussion in 'Spigot Plugin Development' started by joeleoli, Apr 19, 2017.

Thread Status:
Not open for further replies.
  1. I'm working on my hub plugin, which worked previously when it didn't constantly open an inventory for a player, but now that it does constantly keep a menu open, my hub seems to crash after a while.

    How it works:
    [​IMG]

    This menu will always be open while in the hub until you make a selection or are kicked (15 seconds).
    If you close the menu, it re-opens 1 tick later.

    Code (Text):

        public void open(Player player) {
            new BukkitRunnable() {
                public void run() {
                    player.openInventory(inventory); // line 59 of Menu class
                }
            }.runTaskLater(jHub.getInstance(), 1L);
        }
     
    Here are the errors before it crashes:
    Code (Text):
    [23:17:19] [Server thread/WARN]: [jHub] Task #223342 for jHub v1.0 generated an exception
    java.lang.OutOfMemoryError: GC overhead limit exceeded
        at net.minecraft.server.v1_7_R4.ContainerChest.<init>(ContainerChest.java:50) ~[paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.EntityPlayer.openContainer(EntityPlayer.java:640) ~[paperspigot.jar:git-PaperSpigot-36]
        at org.bukkit.craftbukkit.v1_7_R4.entity.CraftHumanEntity.openInventory(CraftHumanEntity.java:193) ~[paperspigot.jar:git-PaperSpigot-36]
        at me.joeleoli.jhub.menu.Menu$1.run(Menu.java:59) ~[?:?]
        at org.bukkit.craftbukkit.v1_7_R4.scheduler.CraftTask.run(CraftTask.java:71) ~[paperspigot.jar:git-PaperSpigot-36]
        at org.bukkit.craftbukkit.v1_7_R4.scheduler.CraftScheduler.mainThreadHeartbeat(CraftScheduler.java:350) [paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.MinecraftServer.v(MinecraftServer.java:696) [paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.DedicatedServer.v(DedicatedServer.java:307) [paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.MinecraftServer.u(MinecraftServer.java:638) [paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.MinecraftServer.run(MinecraftServer.java:544) [paperspigot.jar:git-PaperSpigot-36]
        at net.minecraft.server.v1_7_R4.ThreadServerApplication.run(SourceFile:628) [paperspigot.jar:git-PaperSpigot-36]
    [23:17:32] [Server thread/WARN]: [jHub] Task #223352 for jHub v1.0 generated an exception
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:18:23] [Server thread/WARN]: [jHub] Task #223356 for jHub v1.0 generated an exception
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:19:01] [Snooper Timer/WARN]: Exception in thread "Snooper Timer"
    [23:19:21] [Spigot Watchdog Thread/ERROR]: The server has stopped responding!
    [23:19:56] [Snooper Timer/WARN]: java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:20:38] [Netty IO #3/WARN]: An exception was thrown by a user handler's exceptionCaught() method while handling the following exception:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:20:48] [Netty IO #2/WARN]: An exception was thrown by a user handler's exceptionCaught() method while handling the following exception:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:21:15] [Netty IO #0/WARN]: An exception was thrown by a user handler's exceptionCaught() method while handling the following exception:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    [23:21:47] [Netty IO #2/WARN]: An exception was thrown by a user handler's exceptionCaught() method while handling the following exception:
    java.lang.OutOfMemoryError: GC overhead limit exceeded
    Any ideas?
     
  2. Clearly the server runs out of memory, which we can't help with without the full code. I suggest analyzing the RAM with a tool like jvisualvm to see what goes wrong.
     
    • Like Like x 1
  3. Its just a memory error check, you can disable that by using this "-XX:-UseGCOverheadLimit" as a start value
     
  4. Choco

    Moderator

    1. Holy hell... hop off that dinosaur and stop using 1.7.10...
    2. You're using PaperSpigot. We do not support that here. Your questions are better answered over on the PaperSpigot forums, especially considering they make a lot of changes to the server software imposing various limitations

    @Phloxz sure, that works... but by no means should that limit be reached in the first place. There's clearly something causing that
     
    #4 Choco, Apr 19, 2017
    Last edited: Jan 17, 2019
    • Funny Funny x 2
    • Like Like x 1
    • Agree Agree x 1
  5. Just wondering, @joeleoli why don't you open the inventory directly after it closes? so you don't need the task?
     
    • Agree Agree x 1
  6. Or cancel the event
     
    • Agree Agree x 1
  7. InventoryCloseEvent isn't cancellable.

    Without the tick delay, I am almost sure that errors will occur. That is most likely why he has a tick delay.
     
    • Informative Informative x 1
  8. what errors do you mean?
     
  9. Whats on line 98 in FreezeUtil and on Line 27 in InventoryEvent?
     

  10. Oh no no, I am not having those issues myself, I am just saying that's what happens when you don't have a one tick delay when using openInventory(Inventory); Inside of the InventoryCloseEvent
     
  11. Ehhh, all the code from the post i see is this:

    Code (Text):

     public void open(Player player) {
            new BukkitRunnable() {
                public void run() {
                    player.openInventory(inventory); // line 59 of Menu class
                }
            }.runTaskLater(jHub.getInstance(), 1L);
        }
     
    So would be better if @joeleoli post his lines in here
     
  12. Without a tick delay, you get errors upon joining and disconnecting. I've tried profiling it using visualvm but I've been waiting for hours and it still hasn't crashed. It usually takes like 6+ hours. The GC activity and Heap space is in a fixed pattern, so nothing seems abnormal.
    Inventory close and join listener:
    Code (Text):

        @EventHandler(priority = EventPriority.LOWEST)
        public void onJoin(PlayerJoinEvent event) {
            event.setJoinMessage(null);

            final Player player = event.getPlayer();

            kickTimes.put(player.getUniqueId(), System.currentTimeMillis() + 15000L);

            new BukkitRunnable() {
                public void run() {
                    player.teleport(jHub.getInstance().getSpawn());
                }
            }.runTaskLater(jHub.getInstance(), 1L);

            for(Player p : Bukkit.getOnlinePlayers()) {
                p.hidePlayer(player);
                player.hidePlayer(p);
            }

            player.getInventory().clear();
            player.teleport(jHub.getInstance().getSpawn());

            jHub.getInstance().getMenu().open(player); // Menu is opened here
            jHub.getInstance().getServerList().get("global").setAmount(jHub.getInstance().getServerList().get("global").getAmount() + 1);
        }

       @EventHandler
       public void onInventoryClose(InventoryCloseEvent event) {
           jHub.getInstance().getMenu().open((Player)event.getPlayer());
       }
     
    My Menu class:
    Code (Text):

    package me.joeleoli.jhub.menu;

    import me.joeleoli.jhub.jHub;
    import me.joeleoli.jhub.utils.ItemBuilder;
    import org.bukkit.Bukkit;
    import org.bukkit.ChatColor;
    import org.bukkit.Material;
    import org.bukkit.entity.Player;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.scheduler.BukkitRunnable;

    import java.util.UUID;

    public class Menu {

        private Inventory inventory;

        public Menu() {
            inventory = Bukkit.createInventory(null, 27, ChatColor.AQUA + "Click to connect!");

            inventory.setItem(0, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(1, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(2, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(3, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(4, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(5, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(6, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(7, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(8, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());

            inventory.setItem(9, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(10, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(11, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(12, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(13, new ItemBuilder(Material.DIAMOND_SWORD, ChatColor.AQUA + "Practice " + ChatColor.GRAY + "[US]", ChatColor.GRAY + " » " + ChatColor.AQUA + "Online: " + ChatColor.GRAY + (jHub.getInstance().getServerList().get("Practice").isOnline() ? "True" : "False"), ChatColor.GRAY + " » " + ChatColor.AQUA + "Players: " + ChatColor.GRAY + jHub.getInstance().getServerList().get("Practice").getAmount()).getItem());
            inventory.setItem(14, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(15, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(16, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(17, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());

            inventory.setItem(18, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(19, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(20, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(21, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(22, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(23, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(24, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 0).getItem());
            inventory.setItem(25, new ItemBuilder(Material.STAINED_GLASS_PANE, "", 1, (byte) 3).getItem());
            inventory.setItem(26, new ItemBuilder(Material.INK_SACK, ChatColor.RED + "Leave the network", 1, (byte) 1).getItem());
        }

        public void update() {
            inventory.setItem(13, new ItemBuilder(Material.DIAMOND_SWORD, ChatColor.AQUA + "Practice " + ChatColor.GRAY + "[US]", ChatColor.GRAY + " » " + ChatColor.AQUA + "Online: " + ChatColor.GRAY + (jHub.getInstance().getServerList().get("Practice").isOnline() ? "True" : "False"), ChatColor.GRAY + " » " + ChatColor.AQUA + "Players: " + ChatColor.GRAY + jHub.getInstance().getServerList().get("Practice").getAmount()).getItem());
        }

        public void open(Player player) {
            new BukkitRunnable() {
                public void run() {
                    player.openInventory(inventory);
                }
            }.runTaskLater(jHub.getInstance(), 1L);
        }

    }
     
     
  13. electronicboy

    IRC Staff

    Can't see anything leaky, the thing worth pointing out is as above, you're not actually running out of memory, you're just hitting the maximum amount of memory in a GC run that GC will allow occurring before it cries... (Java wants you to avoid high GC pressure, as that drastically hurts your performance).
    taking a heap-dump might be useful, but if you're running the server on low amounts of memory, might just be a simple case of allocating more memory in order to up the amount of memory that GC has to work.
     
  14. That could explain why it previously worked :oops:
    I'll test it out, thanks =)
     
  15. All i see seems correct, and you are right you have to use a small task at the join event, but you could remove the task at the inventoryClose Event so you get rid of useless tasks that starts to run
     
  16. Will do.

    I checked my hub today when I got home, and it seemed to crash after the same amount of time as before. I'm using the following start script:
    Code (Text):
    java -Xmx4G -Xms2G -jar paperspigot.jar
     
  17. You don't need 2G minimum, lower it
     
  18. This seems questionable
    Code (Text):
    @EventHandler
       public void onInventoryClose(InventoryCloseEvent event) {
           jHub.getInstance().getMenu().open((Player)event.getPlayer());
       }
    So what if the player logs out? I believe that still triggers the inventory close event. The question is whether the inventory close event is also fired when you open an inventory for a player that is offline.
     
  19. That should be done 1 tick later as you cannot open and close an inventory in the same tick.
     
Thread Status:
Not open for further replies.