Xudifsd Rational life

使用swap进行内存优化

2014-05-30
xudifsd

上次优化后,我们的程序又一次被内存问题所烦。上次使用惰性解决了内存消耗的问题,但是由于上次我们只是需要使用AST一次,使用完后就可以将其删除,比较简单。但是现在问题更加复杂了一些。

由于我们做的是符号执行,程序需要像JVM一样在运行中保存应用的所有类。但又由于我们没有对内存中的类的表示也就是AST做任何优化,所以非常耗内存。因此也可以说这次的优化就是在给之前的代码擦屁股。只是因为这次擦得漂亮,而且有一定的通用性,值得写点东西记录下。

和上次一样,这次最耗内存的结构还是那个AST,但是又由于我们需要保存所有的AST,所以之前惰性的优化有些用,但是也只是让我们死得更慢些而已。优化AST不可取就是因为工作量会很大,而且之前的AST经过了很严密的测试,不会有问题,如果优化后AST错了也会很难debug。于是我们想到了直接把不常使用的AST像操作系统那样swap out到磁盘上,在需要时再swap in即可。而且最棒的是我们之前使用Map来存所有的AST,所以这次优化只需要写一个实现了Map接口的类即可,可以做到只需要修改现有的很少代码。

由于实现了Map接口,而且功能是swap一部分内容出内存,因此取名为SwapMap。分离出来的SwapMap的代码见。需要注意的是这个类对使用有很多限制:首先是这个类只有在key很小,value很大时才会起作用,因为它会一直保存key在内存中,而只对value优化。其次是我们虽然将value扫出,但是却没法像操作系统那样将其所使用的内存也收回,只有等待JVM把内存GC,所以使用这个类的其他代码也必须小心地处理引用问题,如果一直有对value的引用,那么即使value被扫出也没法释放内存。当然还有个要求就是value必须实现Serializable接口。

当前这个类只实现了get和put方法,其他方法应该能比较简单地实现,只是由于我们不需要而已。另外需要强调的是这个类的作者不是我,而是我们实验室的YKG

这是我们优化前的运行图,内存使用随着AST的生成一直在增长,最后到1.5G时基本挂掉。

优化前

这是我们优化后的图,基本上能做到只需要很少的内存即可,因为执行有很强的局部性,所以运行时只需要执行的AST在内存中,其他的AST都可以直接扫出。所以曲线会增长一点然后等AST使用完后在被扫出时下降,这样的内存使用比优化前不知道少了多少倍。

优化后

可以看到效果很好嘛(其实就是因为之前太懒造成AST结构太大才能显现这么好的优化效果)。

这样的优化和之前的优化一样,都是对整个结构的优化,效果会非常好,但是这样的机会并不多,对于更加细致的优化也更加需要能力,而且需要十分清楚程序的内部热点。这里推荐下VisualVM,可以很清楚地显示每个对象占用的内存,不过比较坑爹的就是它不会将对象的成员变量也算入该对象所占的内存,除非变量是非引用类型。


下一篇

Comments

Content