提升冒险岛服务端性能

如果您厌倦了服务器每12小时崩溃一次并且不知道该怎么办,则可能是内存问题。修复这些问题不会伤害您,只会增加正常运行时间。与为分配的堆大小提高Xmx相比,这是一个更好的解决方案。
这将仅覆盖一些大的内存泄漏,并且显然不是所有内容都可以直接说明。但是,它将说明为什么这是内存泄漏,应该执行的操作以及如何解决内存泄漏。有了本指南中介绍的内容,您应该运用所学的知识来解决其他漏洞。
内存泄漏:这是在OdinTeh中引入的,这是一个相当大的内存泄漏,甚至每次浪费在抓取暴民技能数据,宠物技能数据上的CPU甚至会浪费一些。当前代码将Object保存到Map <Pair,V>形式的Map中,或者将Pair作为键,并将其他值作为值。

它应该做什么:由Leifde编码的OdinMS中的所有工厂存储都是这样工作的:当调用get方法时,它需要在需要时从Map返回一个对象。如果为null,则程序将继续执行并从XML文件读取。这是一个好方法,建议与大多数缓存一起使用。

它实际上是做什么的:方法containsKey和get两者都使用某种哈希,问题就像在不使用equals方法的对象上使用equals一样,它永远不会返回true。我举一个例子。

Code:

class A {
    public static void main(String[] a) {
        System.out.println((new A()).equals(new A());
    }
}
这将是错误的。这是因为如果没有覆盖的equals方法,它将进入超类并使用Object中定义的equals方法。Object中的equals方法比较其在内存中的存储位置。此处被比较的两个As被存储在不同的存储位置,因此将返回false。
 
将会发生的事情是它将始终存储在Map中,并且经过很长一段时间后,存储将异常大,从而导致大量内存浪费。
 
如何解决:有三种方法可以解决此问题。
最简单的方法是在Pair中实现equals方法。这也将解决所有其他问题。当然,这是额外的代码。我个人不使用此方法,但这将是最好的方法。
 
添加某处(未经测试):

public boolean equals(Object o) {
    if (this.getClass() != o.getClass()) return false;
    if (this == (Pair) o) return true;
    Pair compare = (Pair) o;
    return compare.getLeft() == this.getLeft() && compare.getRight() == this.getRight();
}

解决该问题的第二种方法是将整个内容转换为String。您可以将其转换为字符串,而不是在对中保存两个整数。因此,不要执行新的Pair <Integer,Integer>(arg1,arg2),而是将存储从Map <Pair,V>更改为Map <String,V>(其中V是第二个对象)。更改containsKey以检查String.valueOf(int1 +“” + int2),然后将其保存到Map <String,V>中。下面是一个示例:/ *此文件是OdinMS Maple Story Server的一部分版权所有(C)2008-Pastebin.com

第三种方法是将Pair更改为具有两个值的对象,然后在新类中添加一个equal方法。因此,代替MobSkill的两个整数,您将获得类似

Code:
class MobPair {
    private int x, y;
    public MobPair(int x, int y) {this.x = x; this.y = y;}
    public boolean equals(Object o) {/*this is left as an exercise to the reader*/ return false;}
}

划重点:不要这样,虽然它可能比第二种方法更清晰。



计时器问题(ScheduledFuture)和参考:


内存泄漏:这是一个问题,因为对情人和其他计时器(例如疯子和其他技能)进行了编码。问题是有时对象永远不会真正消失。我在分析堆转储时注意到了这一点(尽管我在从Simon之前就已经听说过这一点),并发现应该长期存在的ScheduledFuture对象仍然存在。使用此功能为我节省了大量内存。

应该发生的情况:当字符断开连接后,应该经过一段时间的垃圾回收才能处理该对象。内存将被重用于其他用途。

实际发生的情况:由于对MapleCharacter对象的硬引用(这些基本上是对另一个对象的普通引用),这些对象从未真正删除,因为垃圾回收实际上仅在不再有硬引用时才删除。这里的问题是两种方法都有硬性引用,因此垃圾收集将无法正常工作。由此,这将缓慢但必定会占用大量内存。进行编程时,程序员应对ScheduledFutures非常小心。

解决方法:为客户端断开连接添加清晰的方法。取消每个计划,然后将它们设置为null。

示例:
MapleCharacter:
Code:
public void clear() {
    client = null; // removes this hard reference
    if (beholderSchedule != null) { beholderSchedule.cancel(true); }
    beholderSchedule = null;
}
MapleClient:
Code:
public void clear() {
    if (player != null) 
        player.clear(); // clears schedules and stuff
    player = null; // removes hard reference here
}

将此应用到每个时间表。不要忘记取消安装计划。删除对客户端和播放器的引用也将修复您在使用时的另一次内存泄漏。


内存泄漏:这是我在分析堆转储时发现的泄漏。不久之后,每个MapleClient的脚本引擎都会变得很高,并且会占用内存。此泄漏的严重程度不如前两次,但仍应予以修复,因为在很长一段时间后,它确实减少了100 MB的使用量(400联机)。
 
应该发生的情况:像计时器一样,它们应该在客户端消失后消失。但是,这些不是。我从来没有真正想知道为什么会发生这种情况(从来没有看过类内部的变量),但是我敢肯定,这与计时器是相同的原因。还有另一个可能的问题,在这里我不会提出解决方案,因为某些脚本无法删除。
 
实际发生的情况:事情不会消失,一段时间后会发生内存泄漏。MapleClient对象开始变得很大,其中最大的部分是为脚本保存的区域。清除它们,然后在用户断开连接时将其设置为null将使其保持低电平。将其设置为null表示可以立即进行垃圾收集。
 
修复方法:

在MapleClient清除中,添加以下内容:
码:
    this.engines.clear(); 
    this.engines = null;
确保完成脚本后将其删除。另外,请确保正确处理所有脚本,并在完成后从列表中删除这些脚本。

实际发生的情况:通过保存一个int而不是保存MapleMonster,可以为每个SpawnPoint对象中的每个对象释放大约300个字节。由于在许多地图中都有许多生成点,因此这可能加起来很多。如果每个生成点节省300字节,最终将节省一吨。

解决方法:
更换私人的MapleMonster怪物;私人最终int怪物;

这还不是全部。现在,您的生成点已损坏,怪物将不再立足。这就是为什么怪物会掉入LocalMS平台的原因。您必须自己立足。因此,向MapleMonster的构造函数中添加一个附加参数,以寻求立足点。然后添加一个this.fh =立足点。添加一个私有的最终int fh; 到顶部。当您生成怪物时,请记住要用立足点生成它。这将留给您做。以前也有使用怪物作为MapleMonster的地方。您必须将其更改为MapleLifeFactory.getMonster(monster)。因为MapleLifeFactory缓存,所以这不会是一个很大的CPU问题。

不要忘记将相同的东西应用于其他类。如果看到未使用的变量或很大的变量,请尝试使其变小。不过不要过度这样做。


内存泄漏:因此,当您在源中缓存字符串数据时,有时它会存储太多数据。例如,项目名称应仅存储项目名称。它还存储描述和其他信息。这浪费了内存和空间。从OdinMS开始,几乎每个来源都存在此泄漏。

应该发生的情况:缓存应仅存储与ID相关联的商品名称,而不存储其他任何商品。调用缓存时,如果存在,它将返回项目ID的名称。如果不存在,它将添加从String.wz加载的XML中的所有项目ID。

实际发生的情况:加载所有项目ID时,由于它会搜索子项的所有子项,因此也会加载文件中的其他所有内容。这导致存储了大量额外信息。例:

码:
         <imgdir name =“ 4032190”> 
            <string name =“名称” value =“橙色蘑菇娃娃” /> 
            <string name =“ desc” value =“一个模仿橙色蘑菇的可爱娃娃,但看起来有些不对…“ /> 
        </ imgdir>
该项目的描述也将被保存,并且由于每个字符占用一些空间,因此很多字符会占用很多空间。另外,由于编写了代码以一次加载所有项目,因此String数据会占用大量空间。

如何解决:有两种简单的方法可以解决此问题。第一种方法是代替从XML加载,而创建一个读取ID并生成SQL数据的解析器。这样做的好处是它重量轻,读取速度更快,并且不占用内存。另外,您可以自己进行修改。这可能是最简单的方法。

第二种方法是仅确保从XML读取时读取的是名称,而不是其他任何内容。这只是几行代码,但是它可以工作,并且比SQL花费更少的时间。SQL的缺点是,每个版本都必须对其进行更新,并且永远不需要更新。

将此技巧应用于许多其他数据缓存。MCDB实际上是应该使用的大量资源。如果无法获取要编码的版本的MCDB,请编码自己的版本。节省了大量内存。


其他可以提性能

1.检查您的异常。开始记录所有这些。到处都有大量的ConcurrentModificationExceptions和NullPointerExceptions。在MapleServerHandler中

Code:
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        if (cause instanceof IOException || cause instanceof ClassCastException) {
            return;
        }
        MapleClient mc = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
        if (mc != null && mc.getPlayer() != null) {
            System.out.println("Exception caused by " + mc.getPlayer().getName());
        }
        cause.printStackTrace();
    }
然后通过读取堆栈跟踪来修复每个异常。大多数空指针都可以通过添加空检查来修复。通常,最好固定空指针的源,但是在这种情况下,实际上并不需要,因为滞后在这里起作用。
2.疾病和增益的编码很差(您还知道疾病和增益应具有相同的增益统计)。当您取消疾病时,所有疾病都将被取消(什么?)。增益也一样。当您取消buff时,它会尝试取消所有buff,然后选择要取消的buff并取消它。这肯定需要重新编码。怪物状态也属于此类别。当您取消怪物状态时,所有怪物状态都会被取消,更不用说毒药已经是有史以来最糟糕的代码了。修复错误的代码可以提高性能。慢速算法不是很受欢迎。
3.能够时使用EnumMaps。与同类产品相比,这具有更好的性能。我将无济于事,因为这需要修改许多类,并且每个类都会有所不同。除非您知道自己在做什么,否则请勿尝试此操作。
4. ConcurrentModificationExceptions不好。修复它们,因为它们会导致线程问题,就像... 
5.使用ReentrantReadWriteLocks而不是ReentrantLock。它们速度更快,但控制较少。同样,它们应该防止更多的僵局。无论如何,由于可以控制锁定,因此ReentrantLocks比同步更有利。
6.当变量恒定时(当它是一个字段时),使用final修饰符。在类和方法中执行此操作无济于事,而使用final则更多地用作表示方法或类无法更改的标记。那里的性能提升可以忽略不计。
7.自己分析堆转储。我在这里未列出一些内存泄漏,因为它们很难描述和修复。任务存在一些泄漏的问题,buff和怪物状态实际上有其自身的泄漏(前面没有描述)。
 

上一篇: 我被高估了。 Fraysa现在在线 弗雷萨的头像 下一篇:冒险岛079版本修改地图怪物部署教程

游戏下载Download