项目内存优化


3/12/2018 Issue

记录一次内存优化的过程

内存优化对比

数据量

外呼名单 10万 白名单 100万 黑名单 500万

JVM 参数

-verbose:gc 
-XX:+HeapDumpOnOutOfMemoryError 
-server 
-Xms1g 
-Xmx1g 
-XX:PermSize=512m 
-XX:SurvivorRatio=2 
-XX:+UseParallelGC
1
2
3
4
5
6
7
8

优化之前

对比耗时

2018-05-31 15:29:49 [INFO]] [pool-1-thread-1] [parseRegulation] 白名单匹配个数 = 25
2018-05-31 15:29:50 [INFO]] [pool-1-thread-1] [parseRegulation] 黑名单匹配个数 = 189
StopWatch '': running time (millis) = 906
-----------------------------------------
ms     %     Task name
-----------------------------------------
00906  100%  对比

...

diffList.size = 99810
telNos.size = 99810
1
2
3
4
5
6
7
8
9
10
11
12

内存消耗

启动时第一次运行解析任务, 并且符合条件的 calloutList 为 99810;

最高使用 762.5 M

持续运行一段时间, 并且使用相同的号码包进行测试的结果

发生了 OOM

优化之后

对比耗时

2018-05-31 15:46:45 [INFO]] [pool-1-thread-1] [dealWhiteAndBlackList] 白名单匹配个数 = 25
2018-05-31 15:46:45 [INFO]] [pool-1-thread-1] [dealWhiteAndBlackList] 白名单匹配个数 = 189
StopWatch '': running time (millis) = 109
-----------------------------------------
ms     %     Task name
-----------------------------------------
00109  100%  对比

...

diffList.size = 99810
telNos.size = 99810
1
2
3
4
5
6
7
8
9
10
11
12

内存消耗

启动时第一次运行解析任务, 并且符合条件的 calloutList 为 99810;

最高 728 M

持续运行一段时间, 并且使用相同的号码包进行测试的结果

优化方案

  1. 使用 BloomFilter代替 DataCache 来存储黑白名单;
  2. 及时清理占用大内存的临时变量;
布隆过滤器

简介:

是一个很长的二进制向量和一系列随机映射函数. 布隆过滤器可以用于检索一个元素是否在一个集合中. 它的优点是空间效率和查询时间都远远超过一般的算法, 缺点是有一定的误识别率和删除困难.

原理:

当一个元素被加入集合时, 通过K个散列函数将这个元素映射成一个位数组中的K个点, 把它们置为1. 检索时, 我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了: 如果这些点有任何一个0, 则被检元素一定不在;如果都是1, 则被检元素很可能在.

优点:

相比于其它的数据结构, 布隆过滤器在空间和时间方面都有巨大的优势. 布隆过滤器存储空间和插入/查询时间都是常数(O(k)). 而且它不存储元素本身, 在某些对保密要求非常严格的场合有优势.

缺点:

一定的误识别率和删除困难.

开发建议

程序的运行会直接影响系统环境的变化, 从而影响 GC 的触发. 若不针对 GC 的特点进行设计和编码, 就会出现内存驻留等一系列负面影响. 为了避免这些影响, 基本的原则就是尽可能地减少垃圾和减少 GC 过程中的开销. 具体措施包括以下几个方面:

  1. 不要显式调用 System.gc()

    此函数建议 JVM 进行主 GC, 虽然只是建议而非一定, 但很多情况下它会触发主 GC, 从而增加主 GC 的频率, 也即增加了间歇性停顿的次数.

  2. 尽量减少临时对象的使用

    临时对象在跳出函数调用后, 会成为垃圾, 少用临时变量就相当于减少了垃圾的产生.

  3. 对象不用时最好显式置为 null

    一般而言, 为 null 的对象都会被作为垃圾处理, 所以将不用的对象显式地设为 null, 有利于 GC 收集器判定垃圾, 从而提高了 GC 的效率.

  4. 尽量少用静态对象变量

    静态变量属于全局变量, 不会被 GC 回收, 它们会一直占用内存.

Last Updated: 7/4/2019, 1:15:31 PM