记一次JAVA 程序优化之旅

参与到一个项目中后,这个项目有一个非常棘手的问题,就是程序需要在初次启动时加载大量数据,高达80G,这会导致成本高筑,不得不使用裸金属服务器进行部署

上个版本现状
tps:1790  rt:12.94ms    80G

目标是 tps 不下降的情况下,尽量使内存占用下降

改进1

首次观察加载数据,发现这个数据具有一定的规律,都是部门或者场地等的代码,重复率较高,每个字符串出现次数平均80次;可以参考霍夫曼压缩算法的逻辑来处理,这里

于是通过构建数据字段生成Pair<String, Integer> 类似这样的数据结构,左边为字符串,右边为代表字符串的数字

当前我们项目字符串上限是60K个, 那么(只这个特例)只要用两个字节的数据就可以表达出这个字符串的Index(数字)

public class IndexArray {

    private byte[] indexArray = new byte[0];

    private final int UNIT;


    public IndexArray(int size) {
        if (size <= 255) {
            UNIT = 1;
        } else if (size <= 65535) {
            UNIT = 2;
        } else if (size <= 16777215) {
            UNIT = 3;
        } else {
            UNIT = 4;
        }
    }

    public void addIndex(int index) {
        byte[] byteArray = new byte[UNIT];
        for (int i = 0; i < UNIT; i++) {
            byteArray[i] = (byte) ((index >> (i * 8)));
        }

        int srcBytesLength = this.indexArray.length;
        byte[] mergedArray = new byte[srcBytesLength + byteArray.length];
        System.arraycopy(this.indexArray, 0, mergedArray, 0, srcBytesLength);
        System.arraycopy(byteArray, 0, mergedArray, srcBytesLength, byteArray.length);

        this.indexArray = mergedArray;
    }
}

这样通过两个字节就表达了至少40个字节的字符串。

通过这个思路,使内存从80G 下降到了 17G

但是遇到了新的挑战,就是tps 下降了,从tps:1700 下降到 1296

改进2

思考发现自己的寻址算法不是很好,是通过把Index的两个字节从字节数组中一个个取出,然后转换成Integer,再来比较的方式处理的,相对耗时;

这里改进为,将要用于比较的Index 转换为两个字节的数据,在IndexArray上按两个字节滑动比较,效率更好

优化后
tps:1352 rt:17.35ms

效率上升10%

改进3

在这个接口的算法逻辑中看到,这是个贪心算法,由于请求中的参数较多,同时又是反复取出比较,导致String 转 Index 这个过程的算法被反复调用,经检查这里也是比较耗时

于是将这个转化过程改进到进入核心算法之前,提前转换

优化后
tps: 1489 rt: 15.71ms

效率再上升10%

改进4

由于使用stopwatch打印关键步骤耗时过程中发现,在调用别的类或者本类中其他方法时,耗时不定但平均下来占比很大;怀疑是java[CMS(Concurrent Mark Sweep)]内存回收导致stop the world的原因导致的耗时,而系统为大内存,计算密集型程序;选型使用G1作为垃圾回收器进行实验

实验参数:

-server -Xmx85g -Xms85g -Xmn1g -XX:+UseG1GC 
-XX:+UnlockExperimentalVMOptions 
-XX:MaxGCPauseMillis=100 
-XX:InitiatingHeapOccupancyPercent=50 
-XX:G1HeapRegionSize=16M 
-XX:G1NewSizePercent=20 
-XX:G1MaxNewSizePercent=30 
-XX:G1ReservePercent=15 
-XX:G1RSetUpdatingPauseTimePercent=5 
-XX:ParallelGCThreads=16 
-XX:ConcGCThreads=8

(参数含义可以查文档获得)

效果比较显著
tps:3369 rt:6.72ms

达到了优化目标

posted @ 2024-07-31 18:31  明月照江江  阅读(19)  评论(0编辑  收藏  举报