分析Java Spring应用程序性能

20

我有一个Spring应用程序,我认为它存在一些瓶颈,所以我想使用性能分析器来测量每个函数执行的时间。你有什么推荐的方法吗?

我正在运行STS,这个项目是一个Maven项目,使用的是Spring 3.0.1。


对于 Spring 应用程序进行性能分析与对其他 Java 应用程序进行性能分析并没有太大的区别。您可能希望扩大问题范围,以获得更广泛的答案选择。 - skaffman
8个回答

22

我使用了Spring AOP来完成这个任务。

有时候我需要获取一些关于项目中某些方法的执行时间信息(例如控制器的方法)。

在servlet xml文件中,我添加了以下内容:

<aop:aspectj-autoproxy/>

另外,我需要创建方面的类:

@Component
@Aspect
public class SystemArchitecture {

    @Pointcut("execution(* org.mywebapp.controller..*.*(..))")
    public void businessController() {
    }
}

并且性能分析方面:

@Component
@Aspect
public class TimeExecutionProfiler {

    private static final Logger logger = LoggerFactory.getLogger(TimeExecutionProfiler.class);

    @Around("org.mywebapp.util.aspects.SystemArchitecture.businessController()")
    public Object profile(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        logger.info("ServicesProfiler.profile(): Going to call the method: {}", pjp.getSignature().getName());
        Object output = pjp.proceed();
        logger.info("ServicesProfiler.profile(): Method execution completed.");
        long elapsedTime = System.currentTimeMillis() - start;
        logger.info("ServicesProfiler.profile(): Method execution time: " + elapsedTime + " milliseconds.");

        return output;
    }

    @After("org.mywebapp.util.aspects.SystemArchitecture.businessController()")
    public void profileMemory() {
        logger.info("JVM memory in use = {}", (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
    }
}

就这些了。当我从我的 Web 应用程序请求页面时,有关方法执行时间和 JVM 内存使用情况的信息会打印到我的 Web 应用程序日志文件中。


1
呵呵,这实际上是我迄今为止使用的相同解决方法。 :-) 它不完全是一个分析器,但可以帮助你走得更远 :-) - niklassaers
说起来,这在我的业务方法上运行良好,但在我的控制器上却不行。你有什么想法为什么会这样? - niklassaers
抱歉,您在问题中没有提到这一点 :) 如果您的问题涉及Web应用程序,并且使用Tomcat Servlet容器,则可以使用SpringSource tc服务器和Spring Insight。 - Yuri.Bulkin
你的applicationContext.xml文件中是否包含context:component-scan/? - Yuri.Bulkin
问题在于你将会把不用于生产的代码打包到你的项目中。这可能会带来负面影响,从版本兼容性开始,到安全性结束。 - Shmarkus
显示剩余3条评论

4

我推荐使用VisualVM进行一般应用程序分析。它从JDK 1.6_10版本开始提供,比Eclipse TPTP更快、更易用。

(如果你的Spring应用程序在应用服务器(例如Tomcat)中工作,可以尝试将其部署到tc Server开发者版(在STS下载中可用)。它具有有趣的监控功能。似乎tc Server开发者版已不再维护。)

更新于2019.02.22。:更新了VisualVM网址(感谢@Jeff)和tc Server信息。个人目前使用Glowroot来监控运行在应用服务器中的Spring应用程序。


谢谢您的建议。我正在使用OS X,但不知何故TPTP似乎在那里不存在。我忘记了tcServer,所以我一定会试试看的 :-) - niklassaers
@Daimon,感谢您更新URL,我已经在答案中进行了更新。 - Csaba_H

3
您可以使用开源的Java分析器,例如Profiler4J:
http://profiler4j.sourceforge.net/
Netbeans附带了内置分析器,Eclipse也具有分析能力,但我发现Profiler4J更易于使用,因为它具有一个漂亮的图表,显示最耗时的方法。在STS(eclipse)中可正常使用,只需按照网站上的说明操作即可。

谢谢你的提示。看起来Profiler4J的最后一个beta版本发布于2006年,这个项目仍然在活跃吗? - niklassaers
看起来它可能已经不再积极开发了,但是几个月前我使用它时它运行良好。我猜它不会随你的项目一起部署,所以如果它作为一次性工具能够正常工作,你就不需要担心了。还有很多其他商业可用的工具,否则就使用附带于Eclipse的工具即可。 - Neal Donnan
嗯,我试了一下,但事实证明尽管我已经为所有类和代理类设置了仪表,但通话图和调用树都没有生成。不过,内存和线程方面的功能正常。 - niklassaers
你按了小快照按钮吗?那应该会填充图表。在 Web 应用程序中,您可以使用工具栏上的按钮清除寄存器,执行要进行分析的操作,然后单击快照按钮以填充该操作的图表。 - Neal Donnan

2

这里有一个通用讨论,介绍了推荐的工具和技术。

基本上,如果您想要了解如何使应用程序更快,那么假设您应该从测量函数花费的时间开始是完全自然的。这是一种自上而下的方法。

还有一种自下而上的方法,在考虑它时同样自然。这不是问时间,而是问主要做什么,以及为什么要这样做。


2
我们开发了基于JMX和Spring AOP的@Profiled注解,用于生产监控(活动调用、调用计数、调用期间花费的时间、异常计数等)。指标通过JMX公开,并可以通过Visual VM / JConsole和监视系统进行收集;我们开发了一个Hyperic HQ插件。
这个@profiled注解与许多其他JMX附加组件一起打包,以便轻松监视常见组件(dbcp、util.concurrent、cxf、jms等),并在http://code.google.com/p/xebia-france/wiki/XebiaManagementExtras下提供商业友好的Apache软件许可证。
希望这有所帮助,
Cyrille(Xebia)

1
这是Yuri在顶部(被选中的答案)的略微修改版本,它自动计算持续时间的总和并按降序排列。总和仅在最后打印。可能会为您节省10分钟。
    @Component
    @Aspect
    public class SystemArchitecture
    {

         @Pointcut("execution(* erp..*.*(..))")
         public void businessController()
         {
         }

         @Pointcut("execution(* TestMain..*.*(..))")
         public void theEnd()
         {
         }
    }



    @Component
    @Aspect
    public class TimeExecutionProfiler
    {

        static Hashtable<String, Long>  ht  = new Hashtable<String, Long>();

        @Around("profiler.SystemArchitecture.businessController()")
        public Object profile(ProceedingJoinPoint pjp) throws Throwable
        {
            long start = System.nanoTime();
            Object output = pjp.proceed();
            long elapsedTime = System.nanoTime() - start;
            String methodName = pjp.getSignature().toString();
            if (ht.get(methodName) == null)
            {
                ht.put(methodName, elapsedTime);
            }
            else
            {
                ht.put(methodName, ht.get(methodName) + elapsedTime);
            }
            // System.out.println(methodName + " : " + elapsedTime + " milliseconds.");

            return output;
        }

        @After("profiler.SystemArchitecture.theEnd()")
        public void profileMemory()
        {
            List<Object> keys = Arrays.asList(ht.keySet().toArray());
            java.util.Collections.sort(keys, new Comparator<Object>()
            {

                @Override
                public int compare(Object arg0, Object arg1)
                {
                    return ht.get(arg1).compareTo(ht.get(arg0));
                }
            });

            System.out.println("totals Used:");
            for (Object name : keys)
            {
                System.out.println("--" + name + " : " + (ht.get(name) / 1000000));
            }
            System.out.println("JVM memory in use = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
        }
    }

1

我喜欢 JRat,虽然像 profiler4j 一样,它似乎没有在积极开发。无论如何,它很容易使用。


使用起来看起来非常简单,但是当我尝试使用它时,从方面织入(aspect weaving)处收到了空指针错误。不知道为什么,当我禁用JRat时,这些错误就消失了。虽然如果这是我的错误也不会感到惊讶,但我不知道是什么触发了它们或者它们来自哪里。:-I - niklassaers

0
你可以使用Java Mission Controls Flight Recorder,它与Java捆绑在一起,来分析你的代码执行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接