Java 可变参数可能引发的性能问题

在这里先看一下我们项目中之前写的日志打印工具

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Logger {
    public static final boolean DEBUG = true;
    private static final String TAG = "Super_LOG";

    private enum Level {
        V, D, I, W, E
    }

    public static void i(Object... message) {
        if (DEBUG) {
            log(Level.I, message);
        }
    }

    private static void log(Level level, Object... message) {
        if (message == null) {
            return;
        }
        StringBuilder sb = new StringBuilder();
        for (Object item : message) {
            sb.append(item).append(" ");
        }
        switch (level) {
            case V:
                Log.v(TAG, sb.toString());
                break;
        }
    }
}

这里省略了部分代码,可以看到,这里传递的参数是一个可变数据,相对于外部调用的话,需要传递日志打印参数就会方便很多,这也是可变参数一个方便之处。在我封装 aar 库的时候,由于混淆类的问题,阴差阳错的查看编译字节码 class 的时候发现了一个问题,先看没有转换之前的类如下:

1
2
3
4
5
6
7
8
fun initSDK(application: Application, appKey: String, appSecret: String, sn: String, deviceId: String, onInitCallback: OnInitCallback? = null) {
        mContext = application.applicationContext
        Logger.i(TAG,"运行统计接口")
        Logger.i(TAG,"调用平台管理器的初始化方法")
        Logger.i(TAG,"实现各个接口的管理器")
        Logger.i(TAG,"记录当前 init 的状态")
        Logger.i(TAG, "init sdk success")
}

接着看下编译后字节码 class ,这里同样省略了部分代码:

1
2
3
4
5
6
7
8
fun initSDK(application: Application, appKey: String, appSecret: String, sn: String, deviceId: String, onInitCallback: OnInitCallback? = null) {
        mContext = application.applicationContext
        Logger.i(TAG,new Object[]{"运行统计接口"})
   			Logger.i(TAG,new Object[]{"调用平台管理器的初始化方法"})
 				Logger.i(TAG,new Object[]{"实现各个接口的管理器"})
 			  Logger.i(TAG,new Object[]{"记录当前 init 的状态"})
  			Logger.i(TAG,new Object[]{"init sdk success"})
}

可以看到使用可变数组参数传递,最后使用的全都是创建了一个数组对象。每一个日志打印都创建了一个数组,通常来说,项目里面日志打印的地方是非常多的,而且如果是在 for 循环中打印的话,将会创建很多对象,而且这些对象也不会再次使用了。可能会导致内存的分配紊乱,最终 App 卡顿等。

总结

所以还是推荐尽量少使用可变数组,不过也要看情况,像这种大量频繁调用情况下,就完全没有必要,像一些简单的初始化的工作传递多个参数的就没有什么问题。

这里就不进行性能的测试了,普通的方式,和可变参数调用的方式,在调用量很多的情况下,性能上是会有一些差距的,具体可以参考如下的链接文章。

参考链接:

https://stackoverflow.com/questions/2426455/javas-varargs-performance

https://www.coder.work/article/1786975