同学做的东西遇到性能问题了,我们就想,
如果可以测出每个方法执行的时间
就可以分析出性能瓶颈在那里了,想了一下,不可能在每个方法调用前后加入时间代码,用代理模式可以实现,面向切面应该也可以实现。
既然别人已经发明好了轮子,就用轮子吧。
接触过一点AspectJ,发现可以实现
只是切入点的通用写法不会,好吧,百度
这样,可以将所有对类 hello.HelloWorld的访问都拦截,在调用之前,或调用之后,统计相应的信息
经过格式化后的结果如下:
execution(void hello.HelloWorld.main(String[])):
{
execution(void hello.HelloWorld.sayHello()):
{
execution(void hello.HelloWorld.sayHello1()):
{
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:65683
}
,
耗时:198282,
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:50084
}
,
耗时:369469,
execution(void hello.HelloWorld.sayHello1()):
{
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:51726
}
,
耗时:151483,
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:49263
}
有了这个类似JSON的数据,
思考:
可以分析出源代码中的调用main{sayHello();sayHello1();sayHello2();}这里的时间获取不到?是因为main方法已经退出啦,没有机会记录
sayHello{sayHello1();sayHello2();}369469
sayHello1{sayHello2();} 151483
sayHello2{};49263
可以方便的分析出那一个方法在那个调用路径下耗时最长。
sayHello2方法,在4种情况下(在复杂的条件下,调用上下文会导致最终)的执行时间是不相同的,65683,50084,51726,49263
这对分析源代码的执行时的性能问题,十分有用,还可以获得传入的参数值和方法的返回值,果真是十分强大
另外,这个东西,分析出来的调用堆栈已经大部分还原了我的方法中内容了,用来反编译,那是相当准确的。
比如:我的sayHello1就是这样写的
public void sayHello1()
{
this.sayHello2();
}
这还不足以说明问题么?
其实可以做得更深入一点,用一个可视化的树来表示调用堆栈,和调用时间,分析各个方法调用时间的平均值,找出异常执行时的传入参数,这对查找BUG和修复BUG都是很有用的,做这样一个性能分析工具,需要的只是时间。
进一步思考,面向切片编程的根本用途就是,在调用一个方法前或后去执行一些特定的操作。在对一些封装包,无法获取源代码,或无权更改相应的业务代码(实际工作是经常遇到),而要对某个类的某些方法添加一些通用的操作时,就有很大的优势。而不用为了每个类加一个统计时间或记录调用参数或一些其实操作,将所有类的每个方法进行一次封装。
更深一点的应用,就应该是Spring对它的一些应用了。
aspectj可以用.aj来写,也可以在.java中用注解来写,灵活。
最终代码在最后,依赖aspectj.jar包
package hello;
如果可以测出每个方法执行的时间
就可以分析出性能瓶颈在那里了,想了一下,不可能在每个方法调用前后加入时间代码,用代理模式可以实现,面向切面应该也可以实现。
既然别人已经发明好了轮子,就用轮子吧。
接触过一点AspectJ,发现可以实现
只是切入点的通用写法不会,好吧,百度
这样,可以将所有对类 hello.HelloWorld的访问都拦截,在调用之前,或调用之后,统计相应的信息
经过格式化后的结果如下:
execution(void hello.HelloWorld.main(String[])):
{
execution(void hello.HelloWorld.sayHello()):
{
execution(void hello.HelloWorld.sayHello1()):
{
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:65683
}
,
耗时:198282,
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:50084
}
,
耗时:369469,
execution(void hello.HelloWorld.sayHello1()):
{
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:51726
}
,
耗时:151483,
execution(void hello.HelloWorld.sayHello2()):
{
}
,
耗时:49263
}
有了这个类似JSON的数据,
思考:
可以分析出源代码中的调用main{sayHello();sayHello1();sayHello2();}这里的时间获取不到?是因为main方法已经退出啦,没有机会记录
sayHello{sayHello1();sayHello2();}369469
sayHello1{sayHello2();} 151483
sayHello2{};49263
可以方便的分析出那一个方法在那个调用路径下耗时最长。
sayHello2方法,在4种情况下(在复杂的条件下,调用上下文会导致最终)的执行时间是不相同的,65683,50084,51726,49263
这对分析源代码的执行时的性能问题,十分有用,还可以获得传入的参数值和方法的返回值,果真是十分强大
另外,这个东西,分析出来的调用堆栈已经大部分还原了我的方法中内容了,用来反编译,那是相当准确的。
比如:我的sayHello1就是这样写的
public void sayHello1()
{
this.sayHello2();
}
这还不足以说明问题么?
其实可以做得更深入一点,用一个可视化的树来表示调用堆栈,和调用时间,分析各个方法调用时间的平均值,找出异常执行时的传入参数,这对查找BUG和修复BUG都是很有用的,做这样一个性能分析工具,需要的只是时间。
进一步思考,面向切片编程的根本用途就是,在调用一个方法前或后去执行一些特定的操作。在对一些封装包,无法获取源代码,或无权更改相应的业务代码(实际工作是经常遇到),而要对某个类的某些方法添加一些通用的操作时,就有很大的优势。而不用为了每个类加一个统计时间或记录调用参数或一些其实操作,将所有类的每个方法进行一次封装。
更深一点的应用,就应该是Spring对它的一些应用了。
aspectj可以用.aj来写,也可以在.java中用注解来写,灵活。
最终代码在最后,依赖aspectj.jar包
package hello;
import org.apache.commons.lang.time.StopWatch;
public aspect World
{
String resultStr = "";
pointcut callMethod() : call(* hello.HelloWorld.*(..));
pointcut afterCallAnyMethod() : execution(* hello.HelloWorld.*(..));
pointcut beforeCallAnyMethod() : execution(* hello.HelloWorld.*(..));
Object around():callMethod()
{
// StopWatch clock = new StopWatch();
// clock.start();
// clock.stop();
// System.out.println(clock.getTime());
long startTime = System.nanoTime();
Object result = proceed();
long endTime = System.nanoTime();
//System.out.print(thisEnclosingJoinPointStaticPart.toShortString());
// System.out.println(thisJoinPointStaticPart.toShortString());
//System.out.print(" called ");
//System.out.print(thisJoinPoint.toShortString());
resultStr+="耗时:" + (endTime - startTime)+",";
System.out.print("耗时:" + (endTime - startTime)+",");
return result;
}
before() :beforeCallAnyMethod()
{
resultStr+=thisJoinPoint.toString()+":{";
System.out.print(thisJoinPoint.toString()+":{");
//System.out.println("开始执行:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
//System.out.println("before call time:"+System.nanoTime());
}
after() returning : afterCallAnyMethod()
{
resultStr+="},";
System.out.print("},");
//System.out.println("执行完成:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
//System.out.println("after call time:"+System.nanoTime());
}
{
String resultStr = "";
pointcut callMethod() : call(* hello.HelloWorld.*(..));
pointcut afterCallAnyMethod() : execution(* hello.HelloWorld.*(..));
pointcut beforeCallAnyMethod() : execution(* hello.HelloWorld.*(..));
Object around():callMethod()
{
// StopWatch clock = new StopWatch();
// clock.start();
// clock.stop();
// System.out.println(clock.getTime());
long startTime = System.nanoTime();
Object result = proceed();
long endTime = System.nanoTime();
//System.out.print(thisEnclosingJoinPointStaticPart.toShortString());
// System.out.println(thisJoinPointStaticPart.toShortString());
//System.out.print(" called ");
//System.out.print(thisJoinPoint.toShortString());
resultStr+="耗时:" + (endTime - startTime)+",";
System.out.print("耗时:" + (endTime - startTime)+",");
return result;
}
before() :beforeCallAnyMethod()
{
resultStr+=thisJoinPoint.toString()+":{";
System.out.print(thisJoinPoint.toString()+":{");
//System.out.println("开始执行:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
//System.out.println("before call time:"+System.nanoTime());
}
after() returning : afterCallAnyMethod()
{
resultStr+="},";
System.out.print("},");
//System.out.println("执行完成:"+thisJoinPoint.toString()+"\t\t\t\tat time:"+System.nanoTime());
//System.out.println("after call time:"+System.nanoTime());
}
}