【问题标题】:Eclipse long build time for java projectjava项目的Eclipse长构建时间
【发布时间】:2017-12-10 08:59:53
【问题描述】:

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

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

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;
}
}

编辑 从假期回来,并试图更多地了解这一点。我现在尝试通过 headless building ala 来构建我的工作空间:

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

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

EDIT2 好吧,我终于找到了一个“解决方案”,但我对此并不满意。 我正在使用eclipse Neon(4.6.2),并且也尝试过切换到氧气,但问题仍然存在。 然后,我尝试在运行 eclipse mars(4.5.2)的旧计算机上构建我的项目,但奇怪的是没有遇到任何构建挂起的情况。所以我已经从我的旧电脑上复制了我的 mars-eclipse,现在清理我的大项目需要 55 秒,清理所有需要 1:15。仍然对构建挂起的原因非常感兴趣,以便我可以使用更新版本的 eclipse。

【问题讨论】:

  • 好吧,既然它似乎没有在编译,那它还有什么用呢?任务管理器/性能将使您大致了解正在使用的计算机资源。分析答案是个好主意;恐怕命令行编译只会告诉您您已经知道的内容。你不需要另一个 IDE,我注意到你没有要求一个。无论你有什么构建过程,编译后都做了什么?
  • 不,它不是一个 maven 项目,它是一个普通的旧 eclipse java 项目
  • 您是否在进度视图中启用了睡眠和“系统”操作的显示?我不确定你如何判断这座建筑需要 12 分钟,但也许那里会出现什么?
  • 嗨@runholen,你的调试模式是不是加载和运行也很慢??

标签: java eclipse build


【解决方案1】:

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

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


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

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

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

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

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

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

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


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


更新:部分线程转储,引起了对问题的关注并导致猜测EGit可能是问题(这里是full thread dump):

"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)

【讨论】:

  • 我真的不知道如何根据这些堆栈跟踪找出问题所在,但也许你可以?我现在已将堆栈跟踪粘贴到 pastebin:编译:pastebin.com/wnyNNiuD 挂起:pastebin.com/esXLxS1g
  • @runholen 很难说,两个线程转储看起来完全不同。在第一个中,编译器正在积极地做一些工作,但在第二个中,它甚至没有出现。相反,我看到了更多的 IO(例如,“egit”出现了)。如果存在明显的性能问题,则模式很可能会重复。例如,如果平均而言,90% 的时间花在一条路径上,那么您应该在 10 次中看到 9 次该模式。鉴于两个线程转储,我不知道哪一个更重要。如果第二个是瓶颈,您可以尝试禁用 egit。如果是第一个,那就无济于事了。
  • 哇,禁用 egit 实际上解决了问题! :-D 构建速度又快了!请相应地更新您的答案,以表明禁用 egit 可以解决问题,我现在授予您的答案:-)
  • @runholen 谢谢,我很高兴听到这个消息!我更新了答案以记录查看它的步骤。该技术也应该对其他人有用,即使他们没有完全相同的问题。
【解决方案2】:

为什么会有 4346 个警告?那可不好。我让我的项目 100% 没有错误和警告。 (Rootitootoot,是的,我知道。)但在我看来,注册所有这些错误消息和字符串可能会产生很大的开销,特别是如果这样做的代码具有很高的“Big O”成本,例如,如果每个警告都检查先前为重复元素生成的警告。 (我不知道是否有。)可能值得调查。警告可能与编译过程的“压力”相关,从而导致延迟。

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

【讨论】:

  • 我的 Eclipse 工作区包含大量代码,大约 120 万行代码,而最大的项目大约有 70 万行代码。第一个代码已经有 10 多年的历史了,所以我们没有时间让它对所有旧代码都 100% 无警告。我猜想引入很多@Suppresswarning 不会加快速度?无论如何,这些警告在那里也是一件好事,因为它给出了某种关于它在哪里编译的状态。而且每次挂在不同的地方也很有意思。
  • 你有没有像PMD这样的插件附加到项目中?如果是,可能会导致延迟。
【解决方案3】:

试试这个:

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

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

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

【讨论】:

    猜你喜欢
    • 2014-05-06
    • 1970-01-01
    • 1970-01-01
    • 2010-09-17
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多