太長的方法是一種壞味道,重構(gòu)時要盡量拆分大方法為小方法。昨天學(xué)習(xí)了JVM的內(nèi)存管理,發(fā)現(xiàn)將大方法切分為小方法還可以提高內(nèi)存的釋放速度,這與JVM的內(nèi)存管理有關(guān)。在方法調(diào)用時,JVM會創(chuàng)建一個stack frame,該方法的參數(shù)和局部變量都存放在stack中,當(dāng)方法調(diào)用結(jié)束時,該方法的stack frame被銷毀,所有的局部變量占用內(nèi)存被釋放。
- private void bigMethod(){
- ClassA p1 = new ClassA();
- ClassA p2 = new ClassA();
- int p3 = 0;
- int p4 =1;
-
- ClassA p5 = new ClassA();
- ClassA p6 = new ClassA();
- int p7 = 0;
- int p8 =1;
-
- }
private void bigMethod(){ClassA p1 = new ClassA();ClassA p2 = new ClassA();int p3 = 0;int p4 =1;//.... some lines for business logicClassA p5 = new ClassA();ClassA p6 = new ClassA();int p7 = 0;int p8 =1;//.... some lines for business logic}
當(dāng)方法bigMethod()被調(diào)用時,JVM會創(chuàng)建8個局部變量,4個為引用類型,4個為原始類型,他們都被分配在方法的stack中。其中引用類型的變量,還有4個對應(yīng)的對象被創(chuàng)建在heap中。當(dāng)bigMethod()調(diào)用結(jié)束后,p1到p8都被銷毀,而引用類型變量對應(yīng)的對象此時還在heap中,由于對象的引用已經(jīng)銷毀,所以這4個對象處于可以被銷毀的狀態(tài),由gc決定何時銷毀。
對bigMethdo重構(gòu)后,
- private void bigMethod(){
- smallMethod1();
- smallMethod2();
- }
- private void smallMethod1(){
- ClassA p1 = new ClassA();
- ClassA p2 = new ClassA();
- int p3 = 0;
- int p4 =1;
-
- }
- private void smallMethod1(){
- ClassA p5 = new ClassA();
- ClassA p6 = new ClassA();
- int p7 = 0;
- int p8 =1;
-
- }
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}
這時調(diào)用bigMethod()會發(fā)生如下事情,1. 調(diào)用方法samllMethod1(),JVM會創(chuàng)建4個局部變量,2個為引用類型,2個為原始類型,他們都被分配在方法的stack中。引用類型的變量還有2個對應(yīng)的對象被創(chuàng)建在heap中。當(dāng)samllMethod1()調(diào)用結(jié)束后,p1到p4都被銷毀,而引用類型變量對應(yīng)的對象此時還在heap中,由于對象的引用已經(jīng)銷毀,所以這2個對象處于
可以被銷毀的狀態(tài),由gc決定何時銷毀。
2.調(diào)用方法samllMethod2(),重復(fù)步驟1的內(nèi)容。
這樣與重構(gòu)前相比,在調(diào)用方法samllMethod2()時已經(jīng)釋放了p1到p4,JVM已經(jīng)回收了這部分內(nèi)存。而更重要的是,在調(diào)用方法samllMethod2()時,在heap中創(chuàng)建的p1,p2對應(yīng)的對象已經(jīng)處于可以銷毀的狀態(tài),這為JVM
提前提供了銷毀p1,p2的對象的機會。如果此時正好heap中內(nèi)存不夠用了,需要運行g(shù)c來銷毀對象,那么p1,p2的對象占用的內(nèi)存就可以被釋放。而在未重勾前,在這個時刻p1,p2的對象還有引用,是不能銷毀的。最極端的情況是此時此刻heap的內(nèi)從不夠用了,也沒有可以銷毀的對象,那么就會發(fā)生outmemory異常,系統(tǒng)crash。
還有一種情況,就是如果在一個線程方法中調(diào)用了wait(),等待某事件發(fā)生來喚醒該線程,那么在該線程方法調(diào)用可能會很久才會結(jié)束,這段時間內(nèi)該方法內(nèi)的局部變量都不能銷毀。如果該線程永遠沒有被喚醒那么方法內(nèi)的局部變量就永遠不能銷毀,這會造成內(nèi)存泄露。
通過將大方法重構(gòu)為小方法可以提前釋放內(nèi)存,可以在某種程度上避免這種情況的發(fā)生