Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom ClassLoader for indexing classes #85

Merged
merged 11 commits into from
Apr 17, 2018
Merged

Custom ClassLoader for indexing classes #85

merged 11 commits into from
Apr 17, 2018

Conversation

rpau
Copy link
Owner

@rpau rpau commented Apr 11, 2018

@cal101 feel free to review it :)

@coveralls
Copy link

coveralls commented Apr 11, 2018

Coverage Status

Coverage increased (+0.1%) to 86.043% when pulling 720e80c on rpau-index into 4788033 on master.

@rpau
Copy link
Owner Author

rpau commented Apr 11, 2018

@cal101 what I have realized is that using ASM (bytecode parser) is probably much faster than using reflection, specially to read the class definition. Now, the critical point is addType in TypesLoaderVisitor.

https://github.com/EsotericSoftware/reflectasm

@cal101
Copy link
Collaborator

cal101 commented Apr 12, 2018

Reflectasm sounds cool but is reflection really a (measured?) problem?
Or do you think about avoiding class loading via class loser and replace that with reflectasm?
I wonder what that means for access of depended classes.

Since I got some strange results when profiling the class loader related code when I profiled that I used some simple timer based counting System.currentTime*. I found the results more reliable.

Regarding the review I will try to find a slot.

import java.io.File;
import java.util.*;

public class ClassPathTree {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The add method looks strange to me. I suggest writing a test that shows application of the class. Especially dealing with different depths ok key pathes.
I would call the keys parameter a keyPath.
I wonder if the outcome of adding

  1. A/B/C
  2. A/D/E
    is correct according to the assumed expected behavior.
    All fields seem to be candidates for "final".
    Telling the syntax of the key would be nice: absolute file path? Relative file path?

Copy link
Owner Author

@rpau rpau Apr 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e36e5a8

}

public IndexedURLClassPath(URL[] urls) {
super(urls);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make copy of array?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you suggest to copy this array?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deep mistrust ;-) The array is writable and we are creating an alias. I don't want to check the control flow to make sure all users behave and don't modify. Immutable data types or defensive copy make it possible to keep it private/unmodifyable. Parallel execution plans make that even more important.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have marked it as final in 5e6fb89
The copy is created by who provides the array ;)



// Map from resource name to URLClassPath to delegate loading that resource to.
private final ClassPathTree index = new ClassPathTree("/", "/", new HashMap<String, ClassPathTree>(), null);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected this declaration at beginning of class.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (!rootPath.endsWith(File.separator)) {
rootPath = rootPath + File.separator;
}
addFilesToIndex(rootPath.length(), root, delegate);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the urlclasspath definition for file archive?
In the past we had .zip accepted. Years ago but would check against the urlclassloader.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URLClasspath belongs to the "misc" classes of the JVM to load resources.
I have excluded zips because I did not about this, but I can include them as well.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest checking how urlclassloader handles non-directories and do the same. If they check for suffixes we can do the same. But I expectvsomething different.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URLConnection urlc = url.openConnection();
InputStream is = urlc.getInputStream();
if (urlc instanceof JarURLConnection) {

is what urclassloader does.
I suggest trying to open each non-directory as jar and ignore errors if it's not a jar.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see your point/contradiction.

Copy link
Owner Author

@rpau rpau Apr 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the JarLoader is not opening a connection, it is iterating the entries. Looking in detail in the code, I am starting to think that I should maintain and index in TypesLoaderVisitor but using the current URLClassLoader, which already is indexing the entries. Then, the pros are:

  • I do not depend on sun.miscpackage, which is not part of the JDK in Java 10.
  • I reduce the TypesLoader part because the classloader is not needed anymore for the changes that you see in the next PR

The cons are:

  • The indexation happens twice.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nevermind. It not indexes the classpath at the begining. Every request is really expensive in the URLClassLoader class

maybeIndexResource(relPath, delegate);
}
File[] directoryEntries = f.listFiles();
assert(directoryEntries != null);
Copy link
Collaborator

@cal101 cal101 Apr 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw ioexception instead of assert. See methods javadoc.

Copy link
Owner Author

@rpau rpau Apr 12, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ops it was because the code was initially copied from the pants project

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// Callers may request the directory itself as a resource, and may
// do so with or without trailing slashes. We do this in a while-loop
// in case the classpath element has multiple superfluous trailing slashes.
if (relPath.endsWith(File.separator)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move before outer if to store only without trailing slashes. Strip trailingvslashes on lookup, too if not already done.
File separator is always char and has constants for string and char.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

private void addTypes(List<String> files, List<SymbolAction> actions, Node node) {
Iterator<String> it = files.iterator();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and other places: why not just use for loop instead of explicit iterator?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

} else {
classLoader = new CachedClassLoader(new IndexedURLClassLoader(cl));
}
SDKFiles = classLoader.getSDKContents("java.lang");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you care for avoiding statics at the moment or do you want deal with them later?
There is some kind of context missing that brackets an execution/run.
I am dreaming of parallel execution here...

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really good point about the parallel execution here :)

Well, I think that I cannot add more statics for now without overcomplicating the code. Indeed with visualVM now, the hotspot is the LoadStaticImportsAction because of the reflection usage there asking for the import class.

What do you mean with a missing context?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to do parallel runs each run needs it's own set of mutable state.
Often ThreadLocals are used but I prefer explicit passing of a kind of "execution context" that stores data global for a run. For single threaded apps the "statics" provide that.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cal, let's work in the parallel part in a different PR. I do not see how to do it easily with the current logic.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. I don't want to work on it now. I just wanted to point out not working too much against it.

Copy link
Collaborator

@cal101 cal101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check comments. Ignore where too picky... ;-)

@rpau
Copy link
Owner Author

rpau commented Apr 12, 2018

@cal101 thanks for your comments. Now I will review and fix one by one :)

BTW: about the performance improvement replacing reflection by ASM: #86

@rpau
Copy link
Owner Author

rpau commented Apr 13, 2018

@cal101 waiting for your approval ;)

@cal101
Copy link
Collaborator

cal101 commented Apr 13, 2018

Will look at it after some sleep, garden work, shopping, working and driving. Maybe even some snack inbetween ;-)

cal101
cal101 previously approved these changes Apr 14, 2018
Copy link
Collaborator

@cal101 cal101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor style stuff, see comments.

@@ -14,35 +14,52 @@
*/
package org.walkmod.javalang.compiler.types;

import java.util.HashMap;
import java.util.Map;
import java.util.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: wild or no wild?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It has been automatically performed by Intellij. I will removed it.

public class SymbolTypesClassLoader extends ClassLoader {
public class CachedClassLoader extends ClassLoader {

public static Map<String, Class<?>> PRIMITIVES = new HashMap<String, Class<?>>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Collections.unmodifyableMap?


/* The search path for classes and resources */
private IndexedURLClassPath ucp;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Move to top of class?

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wildcard?


private final URL[] urls;
private int lastIndexed = 0;
private static URL RT_JAR;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final?

// static block to resolve java.lang package classes
String[] bootPath = System.getProperties().get("sun.boot.class.path").toString()
.split(Character.toString(File.pathSeparatorChar));
for (String lib : bootPath) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File.pathSeparator

.split(Character.toString(File.pathSeparatorChar));
for (String lib : bootPath) {
if (lib.endsWith("rt.jar")) {
File f = new File(lib);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will that work for java 9?
Should we care now or later?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, I would care if rt is not part of java 9

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The internal files rt.jar, tools.jar, and dt.jar have been removed. The content of these files is now stored in a more efficient format in implementation-private files in the lib directory. Class and resource files previously in tools.jar and dt.jar are now always visible via the bootstrap or application class loaders in a JDK image.

😱

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to create a Issue, because it is a different discussion.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#87

@rpau
Copy link
Owner Author

rpau commented Apr 17, 2018

@cal101 could you review the last changes and then approve if you consider? I think that all your styling comments have been addressed.

Copy link
Collaborator

@cal101 cal101 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly style issues.

* @param relPath relative path
* @param delegate value to insert
*/
private void maybeIndexResource(String relPath, URL delegate) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was:

             String cleanRelPath = stripTrailingFileSeparators();
         if (!index.containsKey(cleanRelPath)) {
             index.put(cleanRelPath, delegate);
         }

And using the same strip-Method for lookup method of index, too.
Seems fishy for me to add multiples here but don't strip on lookup.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Theoretically, you could have multiple classes from different jars under the same package and the JDK uses the first that appears in the classpath.



import java.io.File;
import java.util.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wild card.

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wildcard.

public class IndexedURLClassLoader extends ClassLoader {

/* The search path for classes and resources */
private IndexedURLClassPath ucp;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be final.

@rpau rpau merged commit d7da813 into master Apr 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants