This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 268028 - Performance issue in (jpda) MainProjectManager.getMainProject() -> isDependent(lastSelectedProject, current)
Summary: Performance issue in (jpda) MainProjectManager.getMainProject() -> isDependen...
Status: NEW
Alias: None
Product: ide
Classification: Unclassified
Component: Performance (show other bugs)
Version: Dev
Hardware: PC Windows 7
: P3 normal (vote)
Assignee: Tomas Hurka
Depends on:
Reported: 2016-09-14 17:05 UTC by NukemBy
Modified: 2016-09-15 16:20 UTC (History)
0 users

See Also:
Issue Type: DEFECT
Exception Reporter:

self-profiler for getMainProject (84.61 KB, image/jpeg)
2016-09-14 17:05 UTC, NukemBy

Note You need to log in before you can comment on or make changes to this bug.
Description NukemBy 2016-09-14 17:05:16 UTC
Created attachment 162050 [details]
self-profiler for getMainProject

Method MainProjectManager.getMainProject() is called rather frequently in when doing Java development in NB. It is invoked per almost each user action to update UI (enabling/disabling of actions) and is invoked 4 times to start a unit test (2 * 2 threads, each invocation is around 120-150ms), see attached screenshot.

Origin of this issue is ( - Running single simple unit test in debug takes 6+ seconds) and it may happen that it will be resolved by fixing of that issue ( - FileUtil.normalizedRef cache is cleaned too frequently).

So ... what is going on inside:

        public Project getMainProject () {
            Project lastSelectedProject = lastSelectedProjectRef.get();
            Project current = currentProject.get();
            boolean isMain = isMainProject;
            if (isMain && lastSelectedProject != null &&
                    lastSelectedProject != current  &&
  --->              !isDependent(lastSelectedProject, current )) {  <---
                // If there's a main project set, but the current project has no
                // dependency on it, return the current project.
                return lastSelectedProject;
            } else {
                return current;

implementation of isDependent() collects source roots for both projects and checks that way if one project depends on another

    private static boolean isDependent(Project p1, Project p2) {
        Set<URL> p1Roots = getProjectRoots(p1);
        Set<URL> p2Roots = getProjectRoots(p2);

        for (URL root : p2Roots) {
            Set<URL> dependentRoots = SourceUtils.getDependentRoots(root);
            for (URL sr : p1Roots) {
                if (dependentRoots.contains(sr)) {
                    return true;
        return false;

The problem here is that 
- collection of source roots is rather CPU consuming operation (because of FileUtil.normalizeFile())
- 'isDependent' status actually changes infrequently - when configuration of some project change (and that may not happen during the whole day).

I think "the most correct" solution would be listening to changes in configurations of projects - and updating 'isDependent' status upon real changes. If that is technically hard to implement - caching of 'isDependent' status for few seconds would be sufficient (and refreshing it in 'only one background thread' - currently calculation of 'isDependent' is performed in parallel in each background tread which may need the 'getMainProject()').

For reference - one of the call stacks with MainProjectManager.getMainProject()
"Refresh Editor Context"
    at org.openide.filesystems.FileUtil.normalizeFileCached(
    at org.openide.filesystems.FileUtil.normalizeFile(
    at org.netbeans.modules.masterfs.filebasedfs.FileBasedURLMapper.getFileObjects(
    at org.netbeans.modules.masterfs.MasterURLMapper.getFileObjects(
    at org.openide.filesystems.URLMapper.findFileObject(
    at org.netbeans.modules.debugger.jpda.projectsui.MainProjectManager.isDependent(
--> at org.netbeans.modules.debugger.jpda.projectsui.MainProjectManager.getMainProject(
    at org.netbeans.modules.debugger.jpda.projectsui.RunToCursorActionProvider.shouldBeEnabled(
    at org.netbeans.modules.debugger.jpda.projectsui.RunToCursorActionProvider.access$300(
Comment 1 NukemBy 2016-09-15 16:20:48 UTC
Root cause for this bug is here: