sun.misc.URLClassPath 解析

作用:Java 中加载类和查找资源的路径
jdk8
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/8477fd88653c/src/share/classes/sun/misc/URLClassPath.java
jdk9
http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/9b93380c8445/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java
jdk10
http://hg.openjdk.java.net/jdk/jdk10/file/b09e56145e11/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java
jdk11
http://hg.openjdk.java.net/jdk/jdk11/file/1ddf9a99e4ad/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java
jdk12
https://hg.openjdk.java.net/jdk/jdk12/file/06222165c35f/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java
jdk13
http://hg.openjdk.java.net/jdk/jdk13/file/0368f3a073a9/src/java.base/share/classes/jdk/internal/loader/URLClassPath.java

本文针对 jdk8

属性

// 保存所有的查找路径  
	private ArrayList<URL> path = new ArrayList<>();
    Stack<URL> urls = new Stack<>();
		// 初始为空,根据 urls 生成 loaders
    ArrayList<Loader> loaders = new ArrayList<>();
		// 判断路径和 jar 包是否有相应的 Loader,没有才会放入 loaders 和 lmap
    HashMap<String, Loader> lmap = new HashMap<>();

构造方法

/**                                                                                         
 * 根据给定的 URL 创建 URLClassPath                                                                
 * 使用给定的 URL 顺序查找 Resource 和 jar                                                            
 * 如果 URL 以 '/' 结尾则被认定为路径,否则被认定为 jar 文件                                                     
 */                                                                                         
public URLClassPath(URL[] urls, URLStreamHandlerFactory factory, AccessControlContext acc) {
    for (int i = 0; i < urls.length; i++) {                                                 
        path.add(urls[i]);                                                                  
    }                                                                                       
    push(urls);                                                                             
    if (factory != null) {                                                                  
        jarHandler = factory.createURLStreamHandler("jar");                                 
    }                                                                                       
    if (DISABLE_ACC_CHECKING) {                                                             
        this.acc = null;                                                                    
    } else {                                                                                
        this.acc = acc;                                                                     
    }                                                                                       
}                                                                                                                                                                                      
public URLClassPath(URL[] urls) { this(urls, null, null); }                                                                                                                                                                                       
public URLClassPath(URL[] urls, AccessControlContext acc) { this(urls, null, acc); }                                                                                       

URLClassPath 在哪里被使用到

public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {   
    super(parent);                                         
    // this is to make the stack depth consistent with 1.1 
    SecurityManager security = System.getSecurityManager();
    if (security != null) {                                
        security.checkCreateClassLoader();                 
    }                                                      
    acc = AccessController.getContext();                   
    ucp = new URLClassPath(urls, factory, acc);            
}

public URLClassLoader(URL[] urls, ClassLoader parent) {    
    super(parent);                                         
    // this is to make the stack depth consistent with 1.1 
    SecurityManager security = System.getSecurityManager();
    if (security != null) {                                
        security.checkCreateClassLoader();                 
    }                                                      
    this.acc = AccessController.getContext();              
    ucp = new URLClassPath(urls, acc);                     
}

URLClassLoader(URL[] urls, ClassLoader parent, AccessControlContext acc) {                 
    super(parent);                                         
    // this is to make the stack depth consistent with 1.1 
    SecurityManager security = System.getSecurityManager();
    if (security != null) {                                
        security.checkCreateClassLoader();                 
    }                                                      
    this.acc = acc;                                        
    ucp = new URLClassPath(urls, acc);                     
}

public URLClassLoader(URL[] urls) {                        
    super();                                               
    // this is to make the stack depth consistent with 1.1 
    SecurityManager security = System.getSecurityManager();
    if (security != null) {                                
        security.checkCreateClassLoader();                 
    }                                                      
    this.acc = AccessController.getContext();              
    ucp = new URLClassPath(urls, acc);                     
} 

URLClassLoader(URL[] urls, AccessControlContext acc) {      
    super();                                                
    // this is to make the stack depth consistent with 1.1  
    SecurityManager security = System.getSecurityManager(); 
    if (security != null) {                                 
        security.checkCreateClassLoader();                  
    }                                                       
    this.acc = acc;                                         
    ucp = new URLClassPath(urls, acc);                      
}                                                           

jdk1.8中 AppClassLoader 和 ExtClassLoader 都继承于 URLClassLoader,生成 UrlClassPath 的时候:
AppClassLoader 传入"java.class.path"对应的路径
ExtClassLoader 传入"java.ext.dirs" 对应的路径

// 儿子找爹可以通过 *.class.getClassLoader()实现,爹找儿子属于 JVM 实现层级上的东西,随时可能在版本迭代中被删除。
private static native int[] getLookupCacheForClassLoader(ClassLoader loader, String name); 

private synchronized int[] getLookupCache(String name) {                                                      
    if (lookupCacheURLs == null || !lookupCacheEnabled) {                                                     
        return null;                                                                                          
    }                                                                                                         
    int[] cache = getLookupCacheForClassLoader(lookupCacheLoader, name);                                      
    if (cache != null && cache.length > 0) {                                                                  
        int maxindex = cache[cache.length - 1]; // cache[] is strictly ascending.                             
        if (!ensureLoaderOpened(maxindex)) {                                                                  
            if (DEBUG_LOOKUP_CACHE) {                                                                         
                System.out.println("Expanded loaders FAILED " + loaders.size() + " for maxindex=" + maxindex);
            }                                                                                                 
            return null;                                                                                      
        }                                                                                                     
    }                                                                                                         
    return cache;                                                                                             
}                                                                                                             

public Resource getResource(String name, boolean check) {                
    if (DEBUG) {                                                         
        System.err.println("URLClassPath.getResource(\"" + name + "\")");
    }                                                                    
    Loader loader;                                                       
    int[] cache = getLookupCache(name);                                  
    for (int i = 0; (loader = getNextLoader(cache, i)) != null; i++) {   
        Resource res = loader.getResource(name, check);                  
        if (res != null) {                                               
            return res;                                                  
        }                                                                
    }                                                                    
    return null;                                                         
}                                                                        
                                                                         
private synchronized Loader getNextLoader(int[] cache, int index) {                                           
    if (closed) {                                                                                             
        return null;                                                                                          
    }                                                                                                         
    if (cache != null) {                                                                                      
        if (index < cache.length) {                                                                           
            Loader loader = loaders.get(cache[index]);                                                        
            if (DEBUG_LOOKUP_CACHE) {                                                                         
                System.out.println("HASCACHE: Loading from : " + cache[index] + " = " + loader.getBaseURL()); 
            }                                                                                                 
            return loader;                                                                                    
        } else {                                                                                              
            return null; // finished iterating over cache[]                                                   
        }                                                                                                     
    } else {                                                                                                  
        return getLoader(index);                                                                              
    }                                                                                                         
} 

private synchronized Loader getLoader(int index) {                                         
    if (closed) {                                                                          
        return null;                                                                       
    }                                                                                      
    // Expand URL search path until the request can be satisfied or the URL stack is empty.                                                          
    while (loaders.size() < index + 1) {                                                   
        // Pop the next URL from the URL stack                                             
        URL url;                                                                           
        synchronized (urls) {                                                              
            if (urls.empty()) {                                                            
                return null;                                                               
            } else {                                                                       
                url = urls.pop();                                                          
            }                                                                              
        }                                                                                  
        // Skip this URL if it already has a Loader. (Loader may be null in the case where URL has not been opened but is referenced by a JAR index.)                                              
        String urlNoFragString = URLUtil.urlNoFragString(url);                             
        if (lmap.containsKey(urlNoFragString)) {                                           
            continue;                                                                      
        }                                                                                  
        // Otherwise, create a new Loader for the URL.                                     
        Loader loader;                                                                     
        try {                                                                              
            loader = getLoader(url);                                                       
            // If the loader defines a local class path then add the URLs to the list of URLs to be opened.                                      
            URL[] urls = loader.getClassPath();                                            
            if (urls != null) {                                                            
                push(urls);                                                                
            }                                                                              
        } catch (IOException e) {                                                          
            // Silently ignore for now...                                                  
            continue;                                                                      
        } catch (SecurityException se) {                                                   
            // Always silently ignore. The context, if there is one, that this URLClassPath was given during construction will never have permission to access the URL.                                          
            if (DEBUG) {                                                                   
                System.err.println("Failed to access " + url + ", " + se);                 
            }                                                                              
            continue;                                                                      
        }                                                                                  
        // Finally, add the Loader to the search path.                                     
        validateLookupCache(loaders.size(), urlNoFragString);                              
        loaders.add(loader);                                                               
        lmap.put(urlNoFragString, loader);                                                 
    }                                                                                      
    if (DEBUG_LOOKUP_CACHE) {                                                              
        System.out.println("NOCACHE: Loading from : " + index);                            
    }                                                                                      
    return loaders.get(index);                                                             
}

// 根据 url 生成 Loader
private Loader getLoader(final URL url) throws IOException {                  
    try {                                                                     
        return java.security.AccessController.doPrivileged(                   
                new java.security.PrivilegedExceptionAction<Loader>() {       
                    @Override                                                 
                    public Loader run() throws IOException {                  
                        String file = url.getFile();                          
                        if (file != null && file.endsWith("/")) {             
                            if ("file".equals(url.getProtocol())) {           
                                return new FileLoader(url);                   
                            } else {                                          
                                return new Loader(url);                       
                            }                                                 
                        } else {                                              
                            return new JarLoader(url, jarHandler, lmap, acc); 
                        }                                                     
                    }                                                         
                }, acc);                                                      
    } catch (java.security.PrivilegedActionException pae) {                   
        throw (IOException) pae.getException();                               
    }                                                                         
}
// 新添加进去的导致查找缓存失效
private synchronized void validateLookupCache(int index, String urlNoFragString) {                                      
    if (lookupCacheURLs != null && lookupCacheEnabled) {                                                                
        if (index < lookupCacheURLs.length && urlNoFragString.equals(URLUtil.urlNoFragString(lookupCacheURLs[index]))) {
            return;                                                                                                     
        }                                                                                                               
        if (DEBUG || DEBUG_LOOKUP_CACHE) {                                                                              
            System.out.println("WARNING: resource lookup cache invalidated " + "for lookupCacheLoader at " + index);    
        }                                                                                                               
        disableAllLookupCaches(); // lookupCacheEnabled = false;                                                                                       
    }                                                                                                                   
}

// 操作 urls 的几个方法
// getLoader(int index) 中

// 增加查找路径                                                   
public synchronized void addURL(URL url) {                  
    if (closed) {                                           
        return;                                             
    }                                                       
    synchronized (urls) {                                   
        if (url == null || path.contains(url)) {            
            return;                                         
        }                                                   
        // 在 Stack 的头部添加元素          
        urls.add(0, url);                                   
        path.add(url);                                      
        if (lookupCacheURLs != null) {                      
            // 查找缓存是静态的("java.class.path"、"java.ext.dirs"),动态添加的查找缓存导致其不可使用                    
            disableAllLookupCaches();                       
        }                                                   
    }                                                       
}

// java.net.URLClassLoader
private final URLClassPath ucp;
// java.net.URLClassLoader#addURL
protected void addURL(URL url) {  
    ucp.addURL(url);              
}                                 

getLookupCache 查找缓存是为了提高查找速度,作用如下:
如果 lookupCacheURLs包 含{a.jar,b.jar,c.jar,d.jar},并且包“ foo”仅存在于a.jar和c.jar中,则getLookupCache(“ foo / Bar.class”)将返回 {0,2}。
作用和 sun.misc.MetaIndex 差不多
关于:VM.getSavedProperty(“sun.cds.enableSharedLookupCache”)
在这里插入图片描述
sun.cds.enableSharedLookupCache 是 JVM 实现层面的参数。
在这里插入图片描述
URLClassPath 查找缓存相关的代码在 jdk9 中已经被删除
http://openjdk.5641.n7.nabble.com/RFR-M-8061651-Interface-to-the-Lookup-Index-Cache-to-improve-URLClassPath-search-time-td204636.html

参考

https://www.iteye.com/topic/1132986

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

N3verL4nd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值