方法重构与性能优化
太长的方法是一种坏味道,重构时要尽量拆分大方法为小方法。昨天学习了JVM的内存管理,发现将大方法切分为小方法还可以提高内存的释放速度,这与JVM的内存管理有关。在方法调用时,JVM会创建一个stack frame,该方法的参数和局部变量都存放在stack中,当方法调用结束时,该方法的stack frame被销毁,所有的局部变量占用内存被释放。
private void bigMethod(){ ClassA p1 = new ClassA(); ClassA p2 = new ClassA(); int p3 = 0; int p4 =1; //.... some lines for business logic ClassA p5 = new ClassA(); ClassA p6 = new ClassA(); int p7 = 0; int p8 =1; //.... some lines for business logic}当方法bigMethod()被调用时,JVM会创建8个局部变量,4个为引用类型,4个为原始类型,他们都被分配在方法的stack中。其中引用类型的变量,还有4个对应的对象被创建在heap中。当bigMethod()调用结束后,p1到p8都被销毁,而引用类型变量对应的对象此时还在heap中,由于对象的引用已经销毁,所以这4个对象处于可以被销毁的状态,由gc决定何时销毁。
对bigMethdo重构后,
private void bigMethod(){ smallMethod1(); smallMethod2();}private void smallMethod1(){ ClassA p1 = new ClassA(); ClassA p2 = new ClassA(); int p3 = 0; int p4 =1; //.... some lines for business logic}private void smallMethod1(){ ClassA p5 = new ClassA(); ClassA p6 = new ClassA(); int p7 = 0; int p8 =1; //.... some lines for business logic}这时调用bigMethod()会发生如下事情,1. 调用方法samllMethod1(),JVM会创建4个局部变量,2个为引用类型,2个为原始类型,他们都被分配在方法的stack中。引用类型的变量还有2个对应的对象被创建在heap中。当samllMethod1()调用结束后,p1到p4都被销毁,而引用类型变量对应的对象此时还在heap中,由于对象的引用已经销毁,所以这2个对象处于可以被销毁的状态,由gc决定何时销毁。
2.调用方法samllMethod2(),重复步骤1的内容。
这样与重构前相比,在调用方法samllMethod2()时已经释放了p1到p4,JVM已经回收了这部分内存。而更重要的是,在调用方法samllMethod2()时,在heap中创建的p1,p2对应的对象已经处于可以销毁的状态,这为JVM提前提供了销毁p1,p2的对象的机会。如果此时正好heap中内存不够用了,需要运行gc来销毁对象,那么p1,p2的对象占用的内存就可以被释放。而在未重勾前,在这个时刻p1,p2的对象还有引用,是不能销毁的。最极端的情况是此时此刻heap的内从不够用了,也没有可以销毁的对象,那么就会发生outmemory异常,系统crash。
还有一种情况,就是如果在一个线程方法中调用了wait(),等待某事件发生来唤醒该线程,那么在该线程方法调用可能会很久才会结束,这段时间内该方法内的局部变量都不能销毁。如果该线程永远没有被唤醒那么方法内的局部变量就永远不能销毁,这会造成内存泄露。
通过将大方法重构为小方法可以提前释放内存,可以在某种程度上避免这种情况的发生。
1 楼 LookAtPic 2009-01-09 值得学习,我想请问一下,如何学习JVM的?本人菜鸟,有没有相关方面的资料! 2 楼 gbb21 2009-01-10 减少使用长方法的主要目的并不是加速内存回收吧? 3 楼 ftuo 2009-02-17 感觉还有一个好处就是容易代码阅读。 4 楼 TaoistWar 2009-02-17 还有个好处可以复用 5 楼 luckaway 2009-02-19 方法调用也要消耗资源的!
能优化性能---这个要拿测试数据出来了!
6 楼 taupo 2009-02-19 按照楼主的说法,我还可以说,JVM会生成一个方法列表【数组】,方法越多该列表就越大,调用的时候查找就越慢,更占用内存,性能就下降了哦
当然这些都是很微小的影响,理论上可能对性能有影响,但是这点影响你是否能感觉到呢?
所以我觉得对性能是否真正有提升,很难说
我觉得重构方法的主要作用还是在于重用和便于阅读理解
7 楼 sharp_lover 2009-02-20 如果将一个长的方法拆分成好几个小的方法,在调用的时候会显得麻烦。 8 楼 val831201 2009-03-03 这个方法的拆分,应该主要考虑逻辑吧,即一个长方法里实现了几个小功能,分成小方法来实现,会使程序更清晰 9 楼 xiejin2008 2009-03-04 楼主讲起来,看起来蛮有道理的。可不可以让我们看看具体测试数字的体现呢。重构方法。当然蛮好的。思路更加清晰嘛。 10 楼 wzywjy 2009-03-04 频繁的gc也不好把,重构的首要目的应该还是复用,减少代码的重复 11 楼 tinyyea 2009-03-04 ftuo 写道感觉还有一个好处就是容易代码阅读。
TaoistWar 写道还有个好处可以复用
可以复用倒是真的,
容易阅读倒是仁者见仁,看你给方法命名的水平了,名字取得太丑的你还得F3来看看代码,然后又跳回去反而复杂了。 12 楼 danni505 2009-03-05 最近也在思考这个问题:
我们用的是BS结构的MVC开发模式,“业务层+Dao层”的模式,某个模块的一个httpRequest过来就把请求定位到该模块的业务层,然后调DAO层操作数据,然后处理结果返回给浏览器端。
这样有一个问题出现了,当模块逐渐增多的时候,我们的业务、Dao操作就会出现一些重复,或者说是相类似的情况,那么作为整个系统来说,这些是可以通过改善设计而瘦身的,可是到目前为止我还没有找到一个比较好的解决方法?希望各位同仁能说说你们是怎么面对这个问题的,谢谢。 13 楼 javavsnet 2009-03-05 danni505 写道最近也在思考这个问题:
我们用的是BS结构的MVC开发模式,“业务层+Dao层”的模式,某个模块的一个httpRequest过来就把请求定位到该模块的业务层,然后调DAO层操作数据,然后处理结果返回给浏览器端。
这样有一个问题出现了,当模块逐渐增多的时候,我们的业务、Dao操作就会出现一些重复,或者说是相类似的情况,那么作为整个系统来说,这些是可以通过改善设计而瘦身的,可是到目前为止我还没有找到一个比较好的解决方法?希望各位同仁能说说你们是怎么面对这个问题的,谢谢。
你的问题没有具体情况是无法回答的。通常对重复的代码有两种方式来重构,1是继承,把公共的逻辑提出放到父类中 2是组合,把公共的逻辑提出放到单独类中,其他类引用这个类。两种方式各有适用场景。 14 楼 danni505 2009-03-05 谢谢楼上的提点,是的,我也思考了这个问题的使用场景,觉得这个如果从系统层面来看可能难以集中解决,不过对于一个大的模块则可以采用组合方式,继承我觉得不易控制,不赞同使用,比如一个系统的所有报表则可以进行一些集中定义,然后在各个报表实现中调用。这样的话对于所对公共逻辑的定义则显得十分重要,因为这必然会产生高耦合的情况,所以个人觉得正如楼上所说这种设计改良只适用于局部设计。
有不同观点者请跟帖。 15 楼 zhuxing 2009-03-24 taupo 写道按照楼主的说法,我还可以说,JVM会生成一个方法列表【数组】,方法越多该列表就越大,调用的时候查找就越慢,更占用内存,性能就下降了哦
当然这些都是很微小的影响,理论上可能对性能有影响,但是这点影响你是否能感觉到呢?
所以我觉得对性能是否真正有提升,很难说
我觉得重构方法的主要作用还是在于重用和便于阅读理解
赞同taupo说的
方法大会导致过多的临时变量占用栈内存,那调用层次、调用上下文信息就不会???
一般我们遇到stack over flow的时候,大部分是调用层次引起的问题吧,例如死循环^_^
还有:觉得楼主对性能过于敏感了^_^