Eclipse Java 项目的构建时间较长



编辑:解决方案如下:禁用 egit 解决了建筑问题。出于某种原因,egit 会导致构建过程挂起数分钟。

当我需要重建我的 java 项目时,我遇到了问题。我们的项目需要 12 分钟才能在 eclipse 中使用"干净项目"进行编译。它会以某种随机的百分比挂很长时间。理论上应该没有构建周期,我将"使用周期构建时的最大迭代次数"设置为 1 以防万一。 我想我有一个很好的解决方案来找出它停止的地方:我写了一个小程序来分析生成的.class文件,看看它在哪些类文件上停止了这么长时间。所以我编写了附加的程序,该程序按修改日期对生成的.class文件进行排序 猜猜我是否对结果感到惊讶! 构建在 12:20:40 左右开始,在 12:32:40 完成 4458 个类文件中的第一个是 12:20:42 生成的,最后一个是 12:21:22 生成的。 所以整个构建花了 40 秒。那么日食在最后 11 分钟里发出什么哔哔声??

import java.io.File;
import java.util.*;
public class AnalyzeBuildTime {
public static void main(String[] args) throws Exception{
File dir = new File(path_to_project);
Vector<File> v = new Vector<File>();
parse(v,dir);
System.out.println("Nr of files: "+v.size());
Collections.sort(v,new Comparator<File>() {
public int compare(File o1, File o2) {
return new Long(o1.lastModified()).compareTo(o2.lastModified());
}           
});
System.out.println("Listing");
for (File f : v) {
System.out.println(f.getName()+"t"+getTimeString(f.lastModified()));
}
}
private static void parse(Vector<File> v, File f) throws Exception{
if (f.isDirectory()) {
for (File ff : f.listFiles()) {
parse(v,ff);
}
}
else if (f.getName().endsWith(".class")){
v.addElement(f);
}
}
public static String getTimeString(long l){
Calendar c = Calendar.getInstance();
c.setTimeInMillis(l);
return zero(c.get(Calendar.HOUR_OF_DAY))+":"+zero(c.get(Calendar.MINUTE))+":"+zero(c.get(Calendar.SECOND)); 
}
private static String zero(int i) {
if (i < 10) return "0"+i;
return ""+i;
}
}

编辑 从假期回来,并试图更多地理解这一点。我现在尝试通过无头建筑构建我的工作区:

eclipsec.exe -noSplash -application org.eclipse.jdt.apt.core.aptBuild -data c:workspace

这似乎在一段时间内构建良好,显示消息ala"(发现4345警告)编译多样化",然后突然停止并挂起大约4-5分钟,然后继续,继续"(发现4346警告)编译多样化",就好像什么都没发生一样(是的,它每次都会达到不同的警告计数..) 尝试过用jvisualvm分析它,但真的不知道我应该寻找什么。一些"编译器处理任务"线程是按顺序生成的,最后一个"编译器处理任务"线程挂起时似乎处于等待模式,"运行中"为 0ms,当编译最终继续时,该线程停止并启动新的"编译器处理任务"线程。 应该注意的是,从命令行编译时,它会编译我的所有项目,而不仅仅是在 eclipse 中花费大量时间的大项目,而且它似乎在挂在通常的"多样化"或其他项目之前完成了大"Pos"项目。 我刚刚尝试从日食霓虹灯升级到日食氧气,但它似乎具有相同的行为。

编辑2 好吧,我终于找到了一个"解决方案",但我对此并不满意。 我正在使用日食霓虹灯(4.6.2),并且还尝试切换到氧气,但问题仍然存在。 然后,我尝试在运行日食火星(4.5.2)的旧计算机上构建我的项目,奇怪的是没有遇到任何构建挂起。所以我从我的旧电脑上复制了我的火星日食,现在清理我的大项目需要 55 秒,清理所有需要 1:15。仍然对构建挂起的原因非常感兴趣,以便我可以使用较新版本的 eclipse。

更新:原来是由EGit插件引起的。禁用后问题消失了。

(要查看调试此类性能问题的路径,请参阅原始答案的其余部分)


可能有几个原因,我不想猜测。但是,如果你有兴趣知道 Eclipse 在做什么,有一种系统的方法可以找出它,或者至少让你处于更好的位置来解决问题:

  1. 启动 Eclipse 并将 VisualVM 附加到构建过程(在您的情况下,是 Eclipse 本身)。VisualVM 应该是 JDK 安装的一部分。如果没有,您可以免费下载。
  2. 在线程下,有"线程转储"选项,它将创建所有正在运行的线程的堆栈跟踪
  3. 触发构建,并在 VisualVM 中创建线程转储
  4. 分析堆栈跟踪
  5. 并查找模式(即,更频繁出现的堆栈跟踪)

从堆栈跟踪中,您应该希望看到 Eclipse 内部的作用。从统计上讲,即使您只查看很少的堆栈跟踪,也很可能会揭示现有的瓶颈。例如,我记得它向我指出我安装的特定插件(例如 FindBugs)的情况。删除它们再次提高了性能。

如果仍然不清楚发生了什么,您可以使用堆栈跟踪为您的问题添加更多详细信息,这将允许有根据的猜测。在最好的情况下,您甚至可以提交错误报告。(特别是,当你的代码库很大时,它不太可能遇到错误)。

为了证明该技术对于查找 IDE 中的性能问题是有效的,我想提一下,每当操作挂起几秒钟时,IntelliJ 都会自动将堆栈跟踪转储到磁盘。他们鼓励用户针对性能问题提交错误报告,并要求他们附加生成的堆栈跟踪。它表明,这些信息对于熟悉代码库(或显示在堆栈跟踪顶部的插件的代码库)的人来说非常有用。

该技术类似于传统的分析,但它通常更有效地定位瓶颈,尤其是在瓶颈很明显的情况下(在您的方案中可能就是这种情况)。

此外,当您需要寻求帮助时,慢速路径的堆栈跟踪非常有用。在无法访问计算机的情况下分析性能问题很困难,但查看具体示例(由堆栈跟踪给出)可能会改变游戏规则。


可以肯定的是,您应该首先排除 Java 进程接近内存限制。VisualVM 将在"监视"选项卡中列出 GC 活动。我认为这不是您的情况的问题,但是如果应用程序或多或少地进行永久性垃圾收集,则检查堆栈跟踪没有意义。在最坏的情况下,它甚至可能具有误导性。


更新:线程转储的一部分,这引起了人们对问题的注意,并导致猜测EGit可能是问题所在(这里是完整的线程转储):

"Worker-65" #196 prio=5 os_prio=0 tid=0x000000002a5d2800 nid=0x29534 runnable [0x0000000049f8e000]
java.lang.Thread.State: RUNNABLE
at sun.nio.fs.WindowsNativeDispatcher.GetFileAttributesEx0(Native Method)
at sun.nio.fs.WindowsNativeDispatcher.GetFileAttributesEx(Unknown Source)
at sun.nio.fs.WindowsFileAttributes.get(Unknown Source)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Source)
at sun.nio.fs.WindowsFileAttributeViews$Basic.readAttributes(Unknown Source)
at org.eclipse.jgit.util.FileUtils.getFileAttributesBasic(FileUtils.java:662)
at org.eclipse.jgit.util.FS_Win32.getAttributes(FS_Win32.java:192)
at org.eclipse.jgit.treewalk.FileTreeIterator$FileEntry.<init>(FileTreeIterator.java:365)
at org.eclipse.jgit.treewalk.FileTreeIterator.entries(FileTreeIterator.java:242)
at org.eclipse.jgit.treewalk.FileTreeIterator.<init>(FileTreeIterator.java:227)
at org.eclipse.jgit.treewalk.FileTreeIterator.createSubtreeIterator(FileTreeIterator.java:233)
at org.eclipse.jgit.treewalk.AbstractTreeIterator.createSubtreeIterator(AbstractTreeIterator.java:572)
at org.eclipse.jgit.treewalk.TreeWalk.enterSubtree(TreeWalk.java:1210)
at org.eclipse.egit.core.RepositoryUtil.canBeAutoIgnored(RepositoryUtil.java:720)
at org.eclipse.egit.core.Activator$IgnoreDerivedResources$1.visit(Activator.java:646)
at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:64)
at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:75)
at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:75)
at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:75)
at org.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:48)
at org.eclipse.egit.core.Activator$IgnoreDerivedResources.resourceChanged(Activator.java:622)
at org.eclipse.core.internal.events.NotificationManager$1.run(NotificationManager.java:299)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at org.eclipse.core.internal.events.NotificationManager.notify(NotificationManager.java:289)
at org.eclipse.core.internal.events.NotificationManager.broadcastChanges(NotificationManager.java:152)
at org.eclipse.core.internal.resources.Workspace.broadcastPostChange(Workspace.java:374)
at org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:1469)
at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:157)
at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:235)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
"Worker-60" #191 prio=5 os_prio=0 tid=0x000000002a5d6800 nid=0x290c0 in Object.wait() [0x0000000049a8f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Unknown Source)
at org.eclipse.core.internal.jobs.ThreadJob.waitForRun(ThreadJob.java:270)
- locked <0x00000005c2362c50> (a java.lang.Object)
at org.eclipse.core.internal.jobs.ThreadJob.joinRun(ThreadJob.java:197)
at org.eclipse.core.internal.jobs.ImplicitJobs.begin(ImplicitJobs.java:92)
at org.eclipse.core.internal.jobs.JobManager.beginRule(JobManager.java:307)
at org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry.waitForWorkspaceLock(IndexDiffCacheEntry.java:466)
at org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry.access$5(IndexDiffCacheEntry.java:458)
at org.eclipse.egit.core.internal.indexdiff.IndexDiffCacheEntry$5.updateIndexDiff(IndexDiffCacheEntry.java:516)
at org.eclipse.egit.core.internal.indexdiff.IndexDiffUpdateJob.run(IndexDiffUpdateJob.java:75)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)

为什么有 4346 警告?这不可能是好事。我保持我的项目 100% 错误和警告免费。(Rootitootoot,是的,我知道。但在我看来,这可能是巨大的开销,注册所有这些错误消息和字符串,特别是如果这样做的代码具有很高的"大O"成本,例如,如果每个警告检查以前生成的警告是否存在重复元素。(我不知道它是否如此。可能值得调查。警告可能与编译过程的"压力"相关联,从而导致延迟。

为了更好地理解"清理项目"过程,以及各个阶段是什么,我首先在 Eclipse 社区论坛上查看。我发现他们非常有帮助。当然,如果您这样做,在这里发布您在那里发现的内容会对遇到相同问题的其他人有所帮助。

试试这个:

使用给定参数更新 eclipse.ini 文件中的"已安装"Java 路径。

-vm
C:/Program Files/Java/jdk1.8.0_131/bin/javaw.exe

如果发现任何差异,请尝试告诉我差异。

最新更新