Kepler性能分析之M2E调优

Author:CaiEric

简介

在使用Eclipse Kepler v4.3.2(WTP v3.5.2, M2E v1.4.1)开发基于Maven的Java Web项目时,将工程导入到workspace速度非常之慢,而且保存修改的文件也会引发长时间的编译,严重影响了开发人员的工作效率。

通过分析日志,调试,辅以性能分析工具VisualVM, 发现了包括M2E-APT, WTP,M2E的诸多问题,并提交了相应的Patch, 其中有一些已经贡献回开源社区。

本文将介绍对M2E的性能分析,下一篇,将侧重于WTP Validation和M2E-APT的性能优化。

测试

我们的项目共包括11个工程,具备一定的复杂度。 我们选择了Eclipse Kepler和Juno,,分别将项目导入到Workspace,计算Build和Validation全部结束的时间。

测试结果显示,WTP3.5.2存在明显的性能瓶颈,同时Validation多次导入时间波动较大,可能存在某种不确定因素。

另外,将项目导入到Eclipse过程中,进度条长时间停在“Maven Importing Projects”,这说明M2E在处理Maven项目时存在一定的性能问题。

所以,应该把性能调优的重点放在WTP和M2E。

Build Build & Validation

Eclipse Kepler(M2E1.4.1 WTP 3.5.2)5m50s13m-23m

Eclipse Juno(M2E1.3.1 WTP 3.4.2)6m7m-16m

什么是M2E Maven已经是主流的构建工具,其build模型和Eclipse的build模型大相径庭,如何让Eclipse识别并准确地编译Maven项目?两者之间必然存在一种适配的过程。Maven IDE(简称M2E)就是为了解决这个问题开发的Eclipse插件, 它主要负责以下事情:

分析Maven工程的依赖,设置成Java工程的classpath。由Maven builder执行定义在pom.xml中的各个maven 插件。分析Maven工程的依赖

M2E维护了自己的Maven工程模型,并提供了刷新API来管理。当pom.xml初次读取或者相关文件变更时,M2E会进行一次刷新来形成或同步模型。刷新主要是获取workspace工程间的依赖关系,解析工程各自的库依赖。刷新多是阻塞的,后续操作必须等待刷新完成。刷新是传递的,如果某工程的pom改变,该工程及依赖它的工程都需要重新刷新。

执行Maven插件

M2E在导入或更新工程的时候,根据pom中的maven plugin定义,依次根据以下配置来匹配maven plugin的执行方式,形成生命周期配置(LifecycleMapping)。如下图所示。

执行方式包括:

Maven builder 发生在Java builder之后,执行时根据上述生命周期配置依次执行相关maven plugin. 这个过程是单线程执行的。

性能分析及解决方案

不必要的重复刷新问题

在导入工程的时候,从日志中注意到,每个工程被刷新了3-4次,而且刷新工程非常耗时,对11个工程做一个同步的刷新需要50s左右。如果只做一次刷新,理论上来讲可以省掉2分钟左右的时间。

分析

在M2E刷新的方法设置断点,分析得出每次刷新的原因:

M2E-WTP需要为某个output classpathEntry设置一个WTP特需的属性,因而修改了.classpath, M2E认为模型过时再次刷新。 这次刷新仅针对个别web工程。 M2E-WTP是M2E和WTP两大Eclipse项目间的桥接, 用于支持基于Maven的JavaEE工程开发。方案

M2E的刷新其实是一个对POM定义的理解和转译过程,这个过程应该是单向的, M2E解析 pom.xml生成.classpath和.project, 那么为什么要在.classpth和.project改变后再次刷新呢?我们可以考虑让M2E的刷新取消关注.classpth和.project的变更,或者如果必须应对这些文件的变更,我们是否可以引入某些cache 来实现模型的部分刷新(即缓存多次刷新时模型中保持不变的部分)?

将上述分析提交给了M2E社区之后,得到了项目负责人的重点关注,他们同意了第一种方案。我们贡献了以下patch,应用以下patch可减少2次同步刷新,1次异步刷新,这样就保证每个工程在导入时仅需进行一次刷新。这些patch将在M2E 1.6.0 生效。

描述

Bug

取消 .classpath or .project改变时的刷新

https://bugs.eclipse.org/bugs/show_bug.cgi?id=436668

避免导入工程的二次刷新

https://bugs.eclipse.org/bugs/show_bug.cgi?id=436679

避免classpath container initialization的二次刷新

https://bugs.eclipse.org/bugs/show_bug.cgi?id=437493

测试

Build

Build & Validation

Eclipse Kepler(M2E1.4.1 WTP 3.5.2)

3m47s

N/A

测试结果显示省下的时间大概在2分钟,约提升30%。

并发刷新问题

M2E会根据工程间依赖的顺序来依次刷新每个maven 工程。从后台日志看,这个刷新是串行的,并且耗时不少。

Line 11233: 13:14:11.368 [Worker-174] DEBUG o.e.m.c.internal.embedder.MavenImpl – Read Maven project: ***\pom.xml in 4555 ms…Line 11257: 13:14:20.916 [Worker-174] DEBUG o.e.m.c.internal.embedder.MavenImpl – Read Maven project: ***\pom.xml in 889 msLine 11273: 13:14:45.190 [Worker-174] DEBUG o.e.m.c.internal.embedder.MavenImpl – Read Maven project: ***\pom.xml in 20530 ms分析

刷新分为两个阶段,第一阶段是读取pom.xml并建立起workspace现有工程间的相互依赖关系,这个过程很快。第二阶段是深度解析每个工程的直接或间接依赖,这个过程主要是由M2E调用Maven处理,后者根据pom定义从本地maven repo匹配或从remote repo下载依赖所需的artifacts (Jar, pom等等)。这个阶段非常耗时,实际耗费的时间取决于工程的复杂度。在本地maven repo已经有全部缓存的情况下,刷新我们的工程所需时间在1s-23s之间。前面提及一次同步的刷新需要50s左右,如果将这一过程并发,刷新的时间基本等同于最耗时的工程,理论上可以减少半分钟。

方案代替你主持夕阳的葬礼。

Kepler性能分析之M2E调优

相关文章:

你感兴趣的文章:

标签云: