今天在公司遇到了一个问题,需要找一条MQ消息的日志记录,遇到了一些问题,所以把解决问题的思路记下来,分享给大家
直接加到项目中的pom.xml文件中
我们只用到了一行代码,bz2解压关键代码:
真正的登录信息隐藏掉了,哈哈
看到OSS工具类中有直接将文件读取成流的方法,看到这个方法,我有一个大胆的想法。嘿嘿嘿
oss工具类可以将oss上的文件以bety[]数组的形式读取,结果可以是一个bety数组输入流,然后这个比特数组输入流,可以传给BZIP2工具解压,返回值是bz2的解压输入流,这个IO工具里面,方法参数有一个以输入流的方式的入参,大家都是InputStream,很好很强大,都串起来了,根本不用下载文件到本地,代码又省了好多步骤。妙啊!!!
然而,想像很美好,结果很残酷,运行测试类,运行了一会,直接内存溢出了。
不信邪,调大JVM虚拟机内存,再次启动运行,继续爆炸,继续加,继续爆炸。。。。。。
开始分析,为啥会内存溢出,看是再次回顾代码
会不会是ArrayList的锅,这玩意会自动扩容,百度了下,ArrayList自动扩容
数组内元素的个数加1,看是否等于或大于数组长度,等于或大于数组长度,处罚扩容
ArrayList在第一次插入元素add()时分配10(默认)个对象空间。假如有20个数据需要添加,那么会在第11个数据的时候(原始数组容量存满时),按照1.5倍增长;之后扩容会按照1.5倍增长(10、15、22、、、)
看了下文件的大小,此时,读取的文件的大小是1G左右
ArrayList的最大容量是2的31次方减1,应该是一个很大的数字
21 ,大概21亿行,才会爆炸。应该大概也许不是它,应为这个数太大了
但是,ArrayList的扩容机制,让他很浪费内存,我们的需求只是遍历,不是查询,用不到下标,使用
LinkedList足够了,他底层使用的是链表,增删快,查询略慢,不过无所谓,我们只遍历
IOUtil工具类修改如下:
优化了一个点,再跑下试试,依旧还是内存溢出,继续爆炸。。。。。。
继续分析,我是需要每行对比关键字,我为什么要把文件读到List数组中在进行每行的关键字对比,这不相当于我把整个文件都读到内存中了,不爆炸才怪。我真傻,真的
3.0版,究极进化,文件每次只读取一行进行关键字匹配
到此,顺利解决了内存溢出的问题,一次读取一行进项匹配,实现方法也可以用IOUtil类中的BufferedReader类的方法readLine()方法实现,这个是用Scanner 类实现的,是不是很眼熟这个类
没错,就是刚开始学习Java的时候用到的那个类。
- 如果只是遍历,可以使用LinkedList代替ArrayList,减少内存开销
- 内存就是比硬盘速度快,SSD都不行,测试了下和1.0版本方法做对比,1.0版本的大部分时间浪费到了下载和解压,再读取的步骤,通过3.0版本代码的运行时的对比,可以看到,解压的速度也很快,大部分时间是把数据从内存写道硬盘上。
- 我之前错怪CUP了,解压文件原来以为是cpu不给力,结果硬盘的IO才是瓶颈。
- 所以编程要减少IO的消耗,真是太对了,可以减少程序的运行时间,提高程序的反应速度。
- 读取大型文件时,采用一行一行读的办法,不要采用整个文件一下读到内存的方法,妈妈再也不用怕我读文件内存溢出了