Percy

考虑Policy.getPolicy()的原因是什么,因为它将保留对上下文的静态引用并可能导致内存泄漏

java

我只是从org.apache.cxf.common.logging.JDKBugHacks和http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener中读取了一些源代码 。 Java的。为了使我的问题讲得不太广泛。:)我只问其中的一段代码。

           // Calling getPolicy retains a static reference to the context 
            // class loader.
            try {
                // Policy.getPolicy();
                Class<?> policyClass = Class
                    .forName("javax.security.auth.Policy");
                Method method = policyClass.getMethod("getPolicy");
                method.invoke(null);
            } catch (Throwable e) {
                // ignore
            }

但是我不明白这个评论。“调用getPolicy会保留对上下文类加载器的静态引用”。他们试图使用JDKBugHacks来解决它。

更新

我忽略了静态块部分。这里是。这是关键。实际上,它已经缓存了策略。那么为什么还要缓存contextClassLoader?在注释中,它声明自JDK 1.4版起@deprecated-由java.security.Policy取代。

我已经仔细检查了java / security / Policy.java的代码。它确实删除了缓存的类加载器。所以我的怀疑是正确的!:)

@Deprecated
public abstract class Policy {

    private static Policy policy;
    private static ClassLoader contextClassLoader;

    static {
        contextClassLoader = java.security.AccessController.doPrivileged
                (new java.security.PrivilegedAction<ClassLoader>() {
                public ClassLoader run() {
                    return Thread.currentThread().getContextClassLoader();
                }
        });
    };

我还添加了getPolicy源代码。

public static Policy getPolicy() {
    java.lang.SecurityManager sm = System.getSecurityManager();
    if (sm != null) sm.checkPermission(new AuthPermission("getPolicy"));
    return getPolicyNoCheck();
}
static Policy getPolicyNoCheck() {
    if (policy == null) {

        synchronized(Policy.class) {

            if (policy == null) {
                String policy_class = null;
                policy_class = java.security.AccessController.doPrivileged
                    (new java.security.PrivilegedAction<String>() {
                    public String run() {
                        return java.security.Security.getProperty
                            ("auth.policy.provider");
                    }
                });
                if (policy_class == null) {
                    policy_class = "com.sun.security.auth.PolicyFile";
                }

                try {
                    final String finalClass = policy_class;
                    policy = java.security.AccessController.doPrivileged
                        (new java.security.PrivilegedExceptionAction<Policy>() {
                        public Policy run() throws ClassNotFoundException,
                                            InstantiationException,
                                            IllegalAccessException {
                            return (Policy) Class.forName
                                    (finalClass,
                                    true,
                                    contextClassLoader).newInstance();
                        }
                    });
                } catch (Exception e) {
                    throw new SecurityException
                            (sun.security.util.ResourcesMgr.getString
                            ("unable to instantiate Subject-based policy"));
                }
            }
        }
    }
    return policy;
}

其实我更深入,发现一些有趣的事情。最近有人向CXF报告了有关此段代码的org.apache.cxf.common.logging.JDKBugHacks的错误。

为了禁用URL缓存,JDKBugHacks运行:

URL url = new URL("jar:file://dummy.jar!/");
URLConnection uConn = url.openConnection();
uConn.setDefaultUseCaches(false);

设置java.protocol.handler.pkgs系统属性时,在某些情况下(例如,如果文件协议URLStreamHandler是一个Signleton),这可能导致系统类加载器和文件协议处理程序之间出现死锁。除此之外,上面的代码实际上只是为了将defaultUseCaches设置为false,因此实际上可以避免打开连接以加快执行速度。

所以解决方法是

URL url = new URL("jar:file://dummy.jar!/");
URLConnection uConn = new URLConnection(url) { 
@Override 
public void connect() throws IOException { 
 // NOOP
 } 
}; 
uConn.setDefaultUseCaches(false);

JDK或apache cxf出现一些小错误是正常的。通常他们会修复它。javax.security.auth.login.Configuration与Policy存在相同的问题,但尚未弃用。


阅读 408

收藏
2020-12-01

共1个答案

小编典典

Java 6中的Policy类包含对类加载器的静态引用,该类加载器在首次访问该类时会初始化为当前线程上下文类加载器:

private static ClassLoader contextClassLoader;

static {
contextClassLoader =
    (ClassLoader)java.security.AccessController.doPrivileged
            (new java.security.PrivilegedAction() {
            public Object run() {
                return Thread.currentThread().getContextClassLoader();
            }
    });
};

Tomcats生命周期侦听器确保从已知环境中初始化该类,在该环境中,上下文类装入器设置为系统类装入器。如果首先从webapp内部访问此类,它将保留对webapps类加载器的引用。这将防止webapps类获取垃圾,从而导致烫发代空间泄漏。

2020-12-01