1 数据类型使用1.1 使用原生类型,禁止使用Java的包装类型和BigDecimal和BigInteger,包装类型占用的内存比原生类型大,计算时存在装箱拆箱过程,浪费CPU时间。1.2 计算指标时或定义指标时,仔细分析每个字段的取值范围,正确使用数据类型,不要图方便,随意使用数据类型,该用Byte不要使用short,该用short不要使用Int,该用Int不要使用Long,慎重使用String,能将String转出数字类型使用,转为数字类型使用。1.3 多个字段的取值范围很小,可以将这几个字段合并为一个Byte,short或Int,避免申请多个Byte,short或Int浪费存储空间。2 集合使用2.1 存储数据尽可能的使用数组,在知道元素个数范围的情况,请使用数组替代List。 List添加数据时,如果长度不够,长度膨胀时会出现数据复制浪费内存和CPU处理时间时间,因为List的底层存储实现使用的也是数组。2.2 使用支持原生态的集合fastuti等,替换掉java和scala自带的集合类型,因为自带的集合类型操作存在装箱拆箱过程,内存消耗大且浪费CPU处理时间。2.3 判断元素是否存在使用set,禁止使用List,然后调用contain操作,contain操作需要逐个遍历。性能及其底下。Set使用的是hash性能高。2.4 使用List时,定义时必须指定其容量大小,减少List自动扩容的次数,提升性能。2.5 在频繁使用List的IndexOf查找元素位置时,在内存充足的情况下使用空间换时间提供性能。增加Map记录每个元素的位置,直接从map中获取,或者将元素重新编号使用id标示元素,存储在数组中直接数组下标检索。List的Indexof是从头开始逐个遍历查找,性能极其低下。3 集合操作3.1 排序,尽量使用数组排序 Arrays.sort方法,特别不要将数组转换为List。因为List的排序最终调用的也是 Arrays.sort方法。这会带来冗余的操作。3.2 取值,数组的单次按下标取值操作比Map的哈希运行算节约时间。3.3 查找,集合的元素个数比较小小于128个时有序的数组二分查找的速度比Map的hash取值块。集合元素个数很多的场景下Map的hash取值比数组的二分查找快。3.4 Map取值,减少hash执行的次数,当需要根据key是否存在执行不同分支时,使用一次get,替代 contain +get操作,减少hash次数,提升性能。4 数据膨胀在最需要的时候膨胀内存,**回收器才能快速的释放内存。
JAVA的内存管理是用GC控制的,无法准确知道什么时候内存释放。当内存中的数据量大时GC速度很慢。因此为了减少GC的次数,在应用程序最需要进行内存膨胀时,才膨胀。否则占据着内存不释放后续执行的程序内存不够,会带来大量的GC次,甚至FULL GC。例如:有一段程序,需要将对象转出二进制格式存储到磁盘,且中间存在着其他指标运行。开始一进来将对象序列化二进制,保存在内存中,导致内存膨胀一次,接着使用原对象计算其他指标。最后将二进制写入磁盘,将指标写入磁盘。这个过程由于序列化后内存膨胀,导致指标计算时内存不足频繁GC,且GC时间超长。后修改为先计算指标,在对象需要写入磁盘时,才序列化,这样指标计算时内存充足,GC次数和时间都短,原对象在序列化成二进制写入磁盘后,能快速释放,GC能快速释放大量的内存提升性能。
5 数据遍历5.1 一次遍历数据处理完所有不同逻辑,不用分逻辑多次遍历同一批数据。5.2 在数组中计算指标,存在使用时间窗内的元素处理数组每个元素时,将数组排序,使用滑窗的方式基于前一个元素处理的结果处理下一个元素,不要使用逐个遍历挑选符合条件的在计算,这会带来大量的遍历开销。5.3 两个数组关联时,将两个数组按关联字段排序,按数组下标依次关联。不要一个数组的元素为基准,逐个遍历另一个数组获取符合条件的元素,这会带来大量无用的遍历开销。6 数值计算6.1 浮点数计算使用乘法替换除法。6.2 使用移位操作替换乘法操作。6.3 常量或常量间的计算,定义全局变量。避免局部变量重复的申请释放内存空间。7 数据结构7.1 尽量设计使用扁平的数据结构,少使用和尽可能不使用嵌套的数据结构。减少存储空间占用。7.2 维度设计时,维度比指标多很多且维度中的某些维度取值很少就1,2个,需要将维度转换为指标,降低数据条数,达到降低数据量的目的。