What is the reason that Policy.getPolicy() is considered as it will retain a static reference to the context and can cause memory leak

孤街醉人 提交于 2020-01-04 11:52:12

问题


I just read some source code is from org.apache.cxf.common.logging.JDKBugHacks and also in http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/JreMemoryLeakPreventionListener.java. In order to make my question clear not too broad. :) I just ask one piece of code in them.

           // 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
            }

But I didn't understand this comment. "Calling getPolicy retains a static reference to the context class loader". And they trying to use JDKBugHacks to work around it.

UPDATE

I overlooked the static block part. Here it is. This is the key. Actually it already has policy cached. So why cache contextClassLoader also? In comment, it claims @deprecated as of JDK version 1.4 -- Replaced by java.security.Policy.

I have double checked the code of java/security/Policy.java. It really removed the cached classloader. So my doubt is valid! :)

@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();
                }
        });
    };

I also add the getPolicy source code.

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;
}

Actually I dig deeper, I find some interesting thing. Someone report a bug to apache CXF about the org.apache.cxf.common.logging.JDKBugHacks for this piece code recently.

In order for disabling url caching, JDKBugHacks runs:

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

When having the java.protocol.handler.pkgs system property set, that can lead to deadlocks between the system classloader and the file protocol Handler in particular situations (for instance if the file protocol URLStreamHandler is a signleton). Besides that, the code above is really there for the sake of setting defaultUseCaches to false only, so actually opening a connection can be avoided, to speed up the execution.

So the fix is

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

It's normal that JDK or apache cxf to have some minor bugs. And normally they will fix it. javax.security.auth.login.Configuration has the same issues with Policy but it's not Deprecated.


回答1:


The Policy class in java 6 contains a static reference to a classloader that is initialized to the current threads context classloader on the first access to the class:

private static ClassLoader contextClassLoader;

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

Tomcats lifecycle listener is making sure to to initialize this class from within a known environment where the context classloader is set to the system classloader. If this class was first accessed from within a webapp, it would retain a reference to the webapps classloader. This would prevent the webapps classes from getting garbage collected, creating a leak of perm gen space.



来源:https://stackoverflow.com/questions/7057421/what-is-the-reason-that-policy-getpolicy-is-considered-as-it-will-retain-a-sta

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!