Bukkit Scheduler Memory Leakage? [Please help!]

Discussion in 'Plugin Development' started by pookeythekid, Apr 15, 2014.

Thread Status:
Not open for further replies.
  1. Offline

    pookeythekid

    Hey, I have a pretty weird and bad issue here. I've made a new plugin involving Bukkit schedulers which sets a group of worlds' times to their specified amount of ticks, which happens every amount of seconds as specified in the config file.

    The problem with this is that the server's memory is skyrocketing from the moment it starts up, using too much memory to even issue commands in the console or join into the server in-game. I'm pretty sure this is memory leakage, and the only possible cause is the scheduler. I've 100% debugged my code (as far as I'm sure of) and nothing is either wrong with it or can take up so much memory other than Bukkit's code, which is why I assume it's the scheduler's fault.

    I know that this is possible to do, since I've seen tons and tons of auto-broadcasting plugins, and I'm not sure why mine isn't working.

    For reference, here's all of the code to my plugin:
    Code:java
    1. package me.pookeythekid.AlwaysTime;
    2.  
    3. import java.util.ArrayList;
    4. import java.util.HashMap;
    5. import java.util.logging.Logger;
    6.  
    7. import org.bukkit.Bukkit;
    8. import org.bukkit.ChatColor;
    9. import org.bukkit.World;
    10. import org.bukkit.command.Command;
    11. import org.bukkit.command.CommandSender;
    12. import org.bukkit.plugin.PluginDescriptionFile;
    13. import org.bukkit.plugin.java.JavaPlugin;
    14.  
    15. public class Main extends JavaPlugin {
    16.  
    17. public final Logger logger = Logger.getLogger("Minecraft");
    18.  
    19. public HashMap<String, Boolean> constWorlds = new HashMap<String, Boolean>();
    20.  
    21. public ArrayList<String> constWorldsList = new ArrayList<String>();
    22.  
    23. public static int wrldNum = 1;
    24.  
    25.  
    26. @Override
    27. public void onDisable() {
    28.  
    29. PluginDescriptionFile pdf = this.getDescription();
    30.  
    31. logger.info("[" + pdf.getName() + "] " + pdf.getName() + " is now disabled.");
    32.  
    33. }
    34.  
    35.  
    36. @Override
    37. public void onEnable() {
    38.  
    39. PluginDescriptionFile pdf = this.getDescription();
    40.  
    41. logger.info("[" + pdf.getName() + "] " + pdf.getName() + " v" + pdf.getVersion() + " is now enabled.");
    42.  
    43.  
    44. try {
    45.  
    46. saveDefaultConfig();
    47.  
    48. } catch (Exception e) { e.printStackTrace(); }
    49.  
    50.  
    51. constWorlds.clear();
    52.  
    53. constWorldsList.clear();
    54.  
    55. int i = 1;
    56.  
    57. while (getConfig().getString("world" + String.valueOf(i) + ".Name") != null) {
    58.  
    59. constWorlds.put(getConfig().getString("world" + String.valueOf(i) + ".Name"), true);
    60.  
    61. constWorldsList.add(getConfig().getString("world" + String.valueOf(i) + ".Name"));
    62.  
    63. }
    64.  
    65. Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new Runnable() {
    66.  
    67. public void run() {
    68.  
    69. wrldNum = 1;
    70.  
    71. while (wrldNum <= constWorldsList.size()) {
    72.  
    73. if (constWorlds.get(getConfig().getString("world" + String.valueOf(wrldNum) + ".Name")) == true) {
    74.  
    75. World world = Bukkit.getServer().getWorld(getConfig().getString("world" + String.valueOf(wrldNum) + ".Name"));
    76.  
    77. world.setTime(getConfig().getLong("world" + String.valueOf(wrldNum) + ".ticks"));
    78.  
    79. }
    80.  
    81. wrldNum++;
    82.  
    83. }
    84.  
    85. }
    86.  
    87. }, 60 * 20, getConfig().getLong("seconds") * 20);
    88.  
    89.  
    90. }
    91.  
    92.  
    93. public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) {
    94.  
    95. if (commandLabel.equalsIgnoreCase("alwaystime") || commandLabel.equalsIgnoreCase("at")) {
    96.  
    97. if (sender.hasPermission("alwaystime.toggle")) {
    98.  
    99. if (args.length < 2) {
    100.  
    101. sender.sendMessage(ChatColor.YELLOW + "/AlwaysTime toggle <world>");
    102. sender.sendMessage(ChatColor.YELLOW + "/AtReload");
    103.  
    104. } else if (args.length >= 2
    105. && args[0].equalsIgnoreCase("toggle")
    106. && Bukkit.getServer().getWorld(args[1]) != null) {
    107.  
    108. if (constWorlds.get(args[1]) == true) {
    109.  
    110. constWorlds.put(args[1], false);
    111. sender.sendMessage(ChatColor.AQUA + "World " + args[1]
    112. + "'s timer has been " + ChatColor.RED + "stopped.");
    113.  
    114. } else if (constWorlds.get(args[1]) == false
    115. && constWorlds.get(args[1]) != null) {
    116.  
    117. constWorlds.put(args[1], true);
    118. sender.sendMessage(ChatColor.AQUA + "World " + args[1]
    119. + "'s timer has been " + ChatColor.GREEN + "started.");
    120.  
    121. }
    122.  
    123. } else if (!args[0].equalsIgnoreCase("toggle")) {
    124.  
    125. sender.sendMessage(ChatColor.YELLOW + "/AlwaysTime toggle <world>");
    126.  
    127. } else if (Bukkit.getServer().getWorld(args[1]) == null
    128. || constWorlds.get(args[1]) == null) {
    129.  
    130. sender.sendMessage(ChatColor.RED + "World " + args[1] + " either doesn't exist "
    131. + "or you don't have it in your AlwaysTime config file.");
    132.  
    133. }
    134.  
    135. } else if (!(sender.hasPermission("alwaystime.toggle"))) {
    136.  
    137. sender.sendMessage(ChatColor.RED + "You don't have permission to perform this command.");
    138.  
    139. }
    140.  
    141. } else if (commandLabel.equalsIgnoreCase("atreload")) {
    142.  
    143. if (sender.hasPermission("alwaystime.reload")) {
    144.  
    145. reloadConfig();
    146.  
    147. constWorlds.clear();
    148.  
    149. constWorldsList.clear();
    150.  
    151. int i = 1;
    152.  
    153. while (getConfig().getString("world" + String.valueOf(i) + ".Name") != null) {
    154.  
    155. constWorlds.put(getConfig().getString("world" + String.valueOf(i) + ".Name"), true);
    156.  
    157. constWorldsList.add(getConfig().getString("world" + String.valueOf(i) + ".Name"));
    158.  
    159. }
    160.  
    161. Bukkit.broadcastMessage(String.valueOf(constWorldsList.size()));
    162. //Broadcasting the message was to troubleshoot the errors with my
    163. // ArrayList and HashMap
    164.  
    165. sender.sendMessage(ChatColor.GREEN + "All timers have been restarted, and config has been reloaded.");
    166.  
    167. }
    168.  
    169. } else if (sender.hasPermission("alwaystime.reload")) {
    170.  
    171. sender.sendMessage(ChatColor.RED + "You don't have permission to perform this command.");
    172.  
    173. }
    174.  
    175. return true;
    176.  
    177. }
    178.  
    179.  
    180. }
    181.  


    And here's the config file:
    Code:
    # How many seconds should there be between updating the time?
    # If you time it correctly, this setting could be used to cycle
    # through a certain period of time. For example, it could be
    # only day or only night. In order to avoid lag overload,
    # keep this number reasonably high!
    seconds: 60
     
    # This is the list of worlds that will always be a certain time.
    # Follow the pattern to add more worlds. The "ticks" setting in
    # each one of these means the time in game ticks that the world
    # will always be.
    world1:
      Name: PCGames
      ticks: 135500
    world2:
      Name: anotherworld
      ticks: 200
    world3:
      Name: yetanotherworld
      ticks: 0
    Please don't let the length of this thread scare you away. I really need an answer to this!

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 7, 2016
  2. Offline

    unon1100

    I've heard some rumors that BukkitSchedule will be deprecated in favor of BukkitRunnable. Here's how to use BukkitRunnable -

    Code:java
    1. new BukkitRunnable(){
    2. @Override
    3. public void run(){
    4. //Code
    5. }
    6. }.runTaskTimer(plugin, delay, timer);

    or
    Code:java
    1. new BukkitRunnable(){
    2. @Override
    3. public void run(){
    4. //Code
    5. }
    6. }.runTaskLater(plugin, delay);
     
  3. Offline

    pookeythekid

    unon1100 Thanks! But I have a question: What kind of object is "timer?" How do you use it?

    Edit: Oh wait a second... runTaskTimer(), and runTaskLater()... Could you please explain the difference please? And still, please explain the use of timer. Thanks.
     
  4. Offline

    Alshain01

    Timer is a repeating task. Later means not now, after a delay, i.e. later.
     
  5. Offline

    pookeythekid

    Alshain01 Yes that part makes sense. What I'm asking is the function of the timer object. Is that a long/int that is the delay between the re-execution of the code?
     
  6. Offline

    BillyGalbreath

  7. Offline

    pookeythekid

  8. Offline

    pookeythekid

    unon1100 Umm... Actually I have one last question: Do I still use "Bukkit.getScheduler().scheduleSyncRepeatingTask()" for this?
    Example:
    Code:java
    1. Bukkit.getScheduler().scheduleSyncRepeatingTask(this, new BukkitRunnable() {
    2.  
    3. public void run() {
    4.  
    5. Bukkit.broadcastMessage("This is a broadcast.");
    6.  
    7. }
    8.  
    9. }, 0, 200);
    10.  


    Edit: Oh wait a second nevermind. That would defeat the purpose of having the delay option at the end of the BukkitRunnable body. But how would you piece the code together? Just how it is?
     
  9. Offline

    xTrollxDudex

  10. Offline

    pookeythekid

    xTrollxDudex Yeah I noticed that after I posted it, but I just got too lazy to do it. But you get the point.

    BillyGalbreath unon1100 The code you two gave me still lags the server to the max. I'm assuming that this is memory leakage because my server host has once diagnosed this issue with some of the same conditions, such as not being able to log into or connect to the server. My next plan is to just take a look at the code of another (working) scheduled plugin.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 7, 2016
  11. Offline

    BillyGalbreath

    You need to check and make sure you have ONE scheduler per world. If you are creating a new one every time it runs then that would eat up your resources rather quickly. Its the only thing i can think of that might be causing the issue.
     
  12. Offline

    xTrollxDudex

  13. Offline

    L33m4n123

    Maybe I am too sleepy right now. But didn't you created a endless loop with that while(get config!=null) thing?
     
    blablubbabc and ferrybig like this.
  14. Offline

    pookeythekid

    L33m4n123 You may think so, but if you look at the format of the config file, it's "world1: <stuff below>, world2: <stuff below>" and so on. With the while loop, I check if "world" + i (in the first loop this would be "world1") != null, which would return true. As the while loop progresses, then "i" will increase. If the "i" eventually reaches a nonexistent world, which in the current config file would be "world4," then the loop will stop. So no, it's not an infinite loop.

    BillyGalbreath I'm quite sure I'm not creating a scheduler for each world. You see, I have one scheduler that manages all of the worlds. Each time the scheduler fires off, I have a while loop inside of the run() void that loops through the existent worlds, the worlds that were added to the ArrayList in the previous while loop, and sets their time to the ticks that are specified in the config file.

    Actually, a thought just occurred to me... it is slower to get data from an external file, so I could try to get the ticks for each world into another HashMap, then I could give the scheduler that data and perhaps that would speed up the process, using less memory. I kind of doubt it will make too much of a difference, but eh, it's worth a shot.

    Edit: And just a note for everyone, I don't get any stack traces in the console, so I know it's not an error. A while ago, the plugin actually did work, but I got a ton of error spams in the console and the time only worked for one world. But that was a while ago. Now the only problem is some kind of memory overuse.

    Does anyone think that this may work if I combined BukkitRunnable with Bukkit.getScheduler().scheduleSyncRepeatingTask, and just took the delay values out of BukkitRunnable?

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 7, 2016
  15. Offline

    L33m4n123

    You do

    Code:
    int i = 1
    while string of i != null
      add string into hashMap
      add string to list
    
    so. No where you set the string to null NOR do you increase the i-value thus it stays at i = 1 so the string stays the same thus the while loop runs endlessly and in the end lagging your server.
     
  16. Offline

    BillyGalbreath

    Its not reading from file each time. getConfig() is in memory. Its populated when the plugins first loads/reloads (or when you explicitly tell it to reload from file).
     
  17. Offline

    pookeythekid

    L33m4n123 AAAAGGHH!! I feel like such an idiot! Technically you're half-wrong, since I'm not checking if i is null, I'm using i to loop through "world1," "world2," "world3" and so on. But I'm not increasing the i value... DUH! Thanks a lot.

    BillyGalbreath Oh really? I did not know that...

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Jun 7, 2016
Thread Status:
Not open for further replies.

Share This Page