Skip to content

Commit 4e7bd08

Browse files
authored
Merge pull request #9307 from mbien/cache-loading-perf
Improve startup cache loading performance
2 parents 654bb70 + f673ada commit 4e7bd08

File tree

12 files changed

+402
-252
lines changed

12 files changed

+402
-252
lines changed

platform/core.startup/src/org/netbeans/core/startup/ModuleList.java

Lines changed: 97 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,12 @@
2424
import java.io.BufferedInputStream;
2525
import java.io.ByteArrayInputStream;
2626
import java.io.CharArrayWriter;
27+
import java.io.DataInputStream;
2728
import java.io.DataOutputStream;
2829
import java.io.File;
2930
import java.io.FileNotFoundException;
3031
import java.io.IOException;
3132
import java.io.InputStream;
32-
import java.io.ObjectInputStream;
33-
import java.io.ObjectOutputStream;
3433
import java.io.OutputStream;
3534
import java.io.OutputStreamWriter;
3635
import java.io.PushbackInputStream;
@@ -67,6 +66,7 @@
6766
import org.openide.filesystems.FileSystem;
6867
import org.openide.filesystems.FileSystem.AtomicAction;
6968
import org.openide.filesystems.FileUtil;
69+
import org.netbeans.DepUtil;
7070
import org.openide.modules.Dependency;
7171
import org.openide.modules.InstalledFileLocator;
7272
import org.openide.modules.SpecificationVersion;
@@ -111,7 +111,7 @@ final class ModuleList implements Stamps.Updater {
111111
/** to fire events with */
112112
private final Events ev;
113113
/** map from code name (base)s to statuses of modules on disk */
114-
private final Map<String,DiskStatus> statuses = new HashMap<String,DiskStatus>(100);
114+
private final Map<String, DiskStatus> statuses = new HashMap<>(100);
115115
/** whether the initial round has been triggered or not */
116116
private boolean triggered = false;
117117
/** listener for changes in modules, etc.; see comment on class Listener */
@@ -197,8 +197,7 @@ private File findJarByName(String jar, String name) throws IOException {
197197
for (File candidate : jars) {
198198
int candidateMajor = -1;
199199
SpecificationVersion candidateSpec = null;
200-
JarFile jf = new JarFile(candidate);
201-
try {
200+
try (JarFile jf = new JarFile(candidate)) {
202201
java.util.jar.Attributes attr = jf.getManifest().getMainAttributes();
203202
String codename = attr.getValue("OpenIDE-Module");
204203
if (codename != null) {
@@ -211,8 +210,6 @@ private File findJarByName(String jar, String name) throws IOException {
211210
if (sv != null) {
212211
candidateSpec = new SpecificationVersion(sv);
213212
}
214-
} finally {
215-
jf.close();
216213
}
217214
if (newest == null || candidateMajor > major || (spec != null && candidateSpec != null && candidateSpec.compareTo(spec) > 0)) {
218215
newest = candidate;
@@ -434,7 +431,10 @@ private Object processStatusParam(String k, String v) throws NumberFormatExcepti
434431

435432
/** Just checks that all the right stuff is there.
436433
*/
437-
private void sanityCheckStatus(Map<String,Object> m) throws IOException {
434+
private static void sanityCheckStatus(Map<String, Object> m) throws IOException {
435+
if (m.isEmpty()) {
436+
throw new IOException("Must define properties"); // NOI18N
437+
}
438438
String jar = (String) m.get("jar"); // NOI18N
439439
if (jar == null) {
440440
throw new IOException("Must define jar param"); // NOI18N
@@ -612,45 +612,72 @@ private String readTo(InputStream is, char delim) throws IOException {
612612
}
613613
}
614614

615-
final Map<String,Map<String,Object>> readCache() {
615+
final Map<String, Map<String, Object>> readCache() {
616616
InputStream is = Stamps.getModulesJARs().asStream("all-modules.dat"); // NOI18N
617617
if (is == null) {
618618
// schedule write for later
619619
writeCache();
620620
return null;
621621
}
622622
LOG.log(Level.FINEST, "Reading cache all-modules.dat");
623-
try {
624-
ObjectInputStream ois = new ObjectInputStream(is);
625-
626-
Map<String,Map<String,Object>> ret = new HashMap<String, Map<String, Object>>(1333);
627-
while (is.available() > 0) {
628-
Map<String, Object> prop = readStatus(ois, false);
629-
if (prop == null) {
630-
LOG.log(Level.CONFIG, "Cache is invalid all-modules.dat");
631-
return null;
632-
}
633-
Set<?> deps;
634-
try {
635-
deps = (Set<?>) ois.readObject();
636-
} catch (ClassNotFoundException ex) {
637-
throw new IOException(ex);
638-
}
639-
prop.put("deps", deps);
640-
String cnb = (String)prop.get("name"); // NOI18N
641-
ret.put(cnb, prop);
623+
try (DataInputStream dis = new DataInputStream(is)) {
624+
Map<String, Map<String, Object>> cache = new HashMap<>(1333);
625+
while (dis.available() > 0) {
626+
Map<String, Object> props = readProps(dis);
627+
props.put("deps", readDeps(dis));
628+
cache.put((String) props.get("name"), props);
642629
}
643-
644-
645-
is.close();
646-
return ret;
630+
return cache;
647631
} catch (IOException ex) {
648632
LOG.log(Level.INFO, "Cannot read cache", ex);
649633
writeCache();
650634
return null;
651635
}
652636
}
653637

638+
private static Set<Dependency> readDeps(DataInputStream is) throws IOException {
639+
int depCount = is.readInt();
640+
if (depCount < 0) {
641+
throw new IOException("negative count");
642+
} else if (depCount == 0) {
643+
return Set.of();
644+
}
645+
Set<Dependency> deps = new HashSet<>((int) Math.ceil(depCount / 0.75));
646+
for (int i = 0; i < depCount; i++) {
647+
deps.add(DepUtil.read(is));
648+
}
649+
return deps;
650+
}
651+
652+
/// @see #computeProperties(org.netbeans.Module)
653+
private static Map<String, Object> readProps(DataInputStream is) throws IOException {
654+
int propCount = is.readByte();
655+
if (propCount < 0) {
656+
throw new IOException("negative count");
657+
}
658+
Map<String, Object> props = new HashMap<>((int) Math.ceil(propCount / 0.75));
659+
for (int i = 0; i < propCount; i++) {
660+
String entry = is.readUTF();
661+
int split = entry.indexOf('='); // ok, since keys don't contain '='
662+
String key = entry.substring(0, split);
663+
String value = entry.substring(split + 1, entry.length());
664+
// must match computeProperties()
665+
try {
666+
Object val = switch (key) {
667+
case "name", "jar" -> value;
668+
case "enabled", "autoload", "eager", "reloadable" -> Boolean.valueOf(value);
669+
case "startlevel" -> Integer.valueOf(value);
670+
default -> throw new IOException("unknown key " + key);
671+
};
672+
props.put(key, val);
673+
} catch (IllegalArgumentException ex) {
674+
throw new IOException("unexpected value", ex);
675+
}
676+
}
677+
sanityCheckStatus(props);
678+
return props;
679+
}
680+
654681
final void writeCache() {
655682
Stamps.getModulesJARs().scheduleSave(this, "all-modules.dat", false);
656683
}
@@ -661,14 +688,23 @@ public void cacheReady() {
661688

662689
@Override
663690
public void flushCaches(DataOutputStream os) throws IOException {
664-
ObjectOutputStream oss = new ObjectOutputStream(os);
665691
for (Module m : mgr.getModules()) {
666692
if (m.isFixed()) {
667693
continue;
668694
}
669-
Map<String, Object> prop = computeProperties(m);
670-
writeStatus(prop, oss);
671-
oss.writeObject(m.getDependencies());
695+
// props
696+
Map<String, Object> props = computeProperties(m);
697+
os.writeByte(props.size());
698+
for (Entry<String, Object> prop : props.entrySet()) {
699+
os.writeUTF(prop.getKey() + "=" + prop.getValue());
700+
}
701+
702+
// deps
703+
Set<Dependency> deps = m.getDependencies();
704+
os.writeInt(deps.size());
705+
for (Dependency dep : deps) {
706+
DepUtil.write(dep, os);
707+
}
672708
}
673709
}
674710

@@ -694,7 +730,7 @@ private void writeStatus(Map<String, Object> m, OutputStream os) throws IOExcept
694730

695731
// Use TreeMap to sort the keys by name; since the module status files might
696732
// be version-controlled we want to avoid gratuitous format changes.
697-
for (Map.Entry<String, Object> entry: new TreeMap<String, Object>(m).entrySet()) {
733+
for (Map.Entry<String, Object> entry : new TreeMap<>(m).entrySet()) {
698734
String name = entry.getKey();
699735
if (
700736
name.equals("name") || // NOI18N
@@ -749,11 +785,8 @@ public void run() throws IOException {
749785
LOG.fine("ModuleList: (re)writing " + nue.file);
750786
FileLock lock = nue.file.lock();
751787
try {
752-
OutputStream os = nue.file.getOutputStream(lock);
753-
try {
788+
try (OutputStream os = nue.file.getOutputStream(lock)) {
754789
writeStatus(nue.diskProps, os);
755-
} finally {
756-
os.close();
757790
}
758791
} finally {
759792
lock.releaseLock();
@@ -944,12 +977,14 @@ private void moduleChanged(Module m, DiskStatus status) {
944977
}
945978
}
946979

947-
/** Compute what properties we would want to store in XML
980+
/**
981+
* Compute what properties we would want to store in XML (or the startup cache)
948982
* for this module. I.e. 'name', 'reloadable', etc.
983+
* @see #readProps(java.io.DataInputStream)
949984
*/
950-
private Map<String,Object> computeProperties(Module m) {
985+
private Map<String, Object> computeProperties(Module m) {
951986
if (m.isFixed() || ! m.isValid()) throw new IllegalArgumentException("fixed or invalid: " + m); // NOI18N
952-
Map<String,Object> p = new HashMap<String,Object>();
987+
Map<String, Object> p = new HashMap<>(8);
953988
p.put("name", m.getCodeNameBase()); // NOI18N
954989
if (!m.isAutoload() && !m.isEager()) {
955990
p.put("enabled", m.isEnabled()); // NOI18N
@@ -960,8 +995,7 @@ private Map<String,Object> computeProperties(Module m) {
960995
if (m.getStartLevel() > 0) {
961996
p.put("startlevel", m.getStartLevel()); // NOI18N
962997
}
963-
if (m.getHistory() instanceof ModuleHistory) {
964-
ModuleHistory hist = (ModuleHistory) m.getHistory();
998+
if (m.getHistory() instanceof ModuleHistory hist) {
965999
p.put("jar", hist.getJar()); // NOI18N
9661000
}
9671001
return p;
@@ -990,6 +1024,7 @@ private final class Listener implements PropertyChangeListener, ErrorHandler, En
9901024
// Property change coming from ModuleManager or some known Module.
9911025

9921026
private boolean listening = true;
1027+
@Override
9931028
public void propertyChange(PropertyChangeEvent evt) {
9941029
if (! triggered) throw new IllegalStateException("Property change before trigger()"); // NOI18N
9951030
// REMEMBER this is inside *read* mutex, it is forbidden to even attempt
@@ -1036,15 +1071,19 @@ public void propertyChange(PropertyChangeEvent evt) {
10361071

10371072
// SAX stuff.
10381073

1074+
@Override
10391075
public void warning(SAXParseException e) throws SAXException {
10401076
LOG.log(Level.WARNING, null, e);
10411077
}
1078+
@Override
10421079
public void error(SAXParseException e) throws SAXException {
10431080
throw e;
10441081
}
1082+
@Override
10451083
public void fatalError(SAXParseException e) throws SAXException {
10461084
throw e;
10471085
}
1086+
@Override
10481087
public InputSource resolveEntity(String pubid, String sysid) throws SAXException, IOException {
10491088
if (pubid.equals(PUBLIC_ID)) {
10501089
if (VALIDATE_XML) {
@@ -1062,6 +1101,7 @@ public InputSource resolveEntity(String pubid, String sysid) throws SAXException
10621101

10631102
// Changes in Modules/ folder.
10641103

1104+
@Override
10651105
public void fileDeleted(FileEvent ev) {
10661106
if (isOurs(ev)) {
10671107
if (LOG.isLoggable(Level.FINE)) {
@@ -1073,6 +1113,7 @@ public void fileDeleted(FileEvent ev) {
10731113
fileDeleted0(fo.getName(), fo.getExt()/*, ev.getTime()*/);
10741114
}
10751115

1116+
@Override
10761117
public void fileDataCreated(FileEvent ev) {
10771118
if (isOurs(ev)) {
10781119
if (LOG.isLoggable(Level.FINE)) {
@@ -1084,6 +1125,7 @@ public void fileDataCreated(FileEvent ev) {
10841125
fileCreated0(fo.getName(), fo.getExt()/*, ev.getTime()*/);
10851126
}
10861127

1128+
@Override
10871129
public void fileRenamed(FileRenameEvent ev) {
10881130
if (isOurs(ev)) {
10891131
throw new IllegalStateException("I don't rename anything! " + ev); // NOI18N
@@ -1124,6 +1166,7 @@ private void fileDeleted0(String name, String ext/*, long time*/) {
11241166
} // else ignore
11251167
}
11261168

1169+
@Override
11271170
public void fileChanged(FileEvent ev) {
11281171
if (isOurs(ev)) {
11291172
if (LOG.isLoggable(Level.FINE)) {
@@ -1150,9 +1193,11 @@ public void fileChanged(FileEvent ev) {
11501193
} // else ignore
11511194
}
11521195

1196+
@Override
11531197
public void fileFolderCreated(FileEvent ev) {
11541198
// ignore
11551199
}
1200+
@Override
11561201
public void fileAttributeChanged(FileAttributeEvent ev) {
11571202
// ignore
11581203
}
@@ -1549,7 +1594,7 @@ public void run() {
15491594
Map<String, Map<String, Object>> cache = readCache();
15501595
String[] names;
15511596
if (cache != null) {
1552-
names = cache.keySet().toArray(new String[cache.size()]);
1597+
names = cache.keySet().toArray(String[]::new);
15531598
} else {
15541599
FileObject[] children = folder.getChildren();
15551600
List<String> arr = new ArrayList<String>(children.length);
@@ -1572,7 +1617,7 @@ public void run() {
15721617
LOG.fine("Strange file encountered in modules folder: " + f);
15731618
}
15741619
}
1575-
names = arr.toArray(new String[0]);
1620+
names = arr.toArray(String[]::new);
15761621
}
15771622
ev.log(Events.MODULES_FILE_SCANNED, names.length);
15781623
XMLReader reader = null;
@@ -1635,12 +1680,12 @@ public void run() {
16351680
}
16361681
ModuleHistory history = new ModuleHistory(jar, "loaded from " + f); // NOI18N
16371682
Boolean reloadableB = (Boolean) props.get("reloadable"); // NOI18N
1638-
boolean reloadable = reloadableB != null ? reloadableB.booleanValue() : false;
1639-
boolean enabled = enabledB != null ? enabledB.booleanValue() : false;
1683+
boolean reloadable = reloadableB != null ? reloadableB : false;
1684+
boolean enabled = enabledB != null ? enabledB : false;
16401685
Boolean autoloadB = (Boolean) props.get("autoload"); // NOI18N
1641-
boolean autoload = autoloadB != null ? autoloadB.booleanValue() : false;
1686+
boolean autoload = autoloadB != null ? autoloadB : false;
16421687
Boolean eagerB = (Boolean) props.get("eager"); // NOI18N
1643-
boolean eager = eagerB != null ? eagerB.booleanValue() : false;
1688+
boolean eager = eagerB != null ? eagerB : false;
16441689
NbInstaller.register(name, props.get("deps")); // NOI18N
16451690
Integer startLevel = (Integer)props.get("startlevel"); // NOI18N
16461691
Module m = createModule(jarFile, history, reloadable, autoload, eager, startLevel);

0 commit comments

Comments
 (0)