По какой причине рассматривается Policy.getPolicy (), поскольку он будет сохранять статическую ссылку на контекст и может вызвать утечку памяти - PullRequest
2 голосов
/ 14 августа 2011

Я только что прочитал некоторый исходный код из 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 - Заменен 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;
}

На самом деле я копаю глубже, я нахожу кое-что интересное.Кто-то недавно сообщил об ошибке в Apache 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 является сигнальным символом).Кроме того, приведенный выше код на самом деле существует только для установки 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 имеет те же проблемы с политикой, но не устарела.

1 Ответ

3 голосов
/ 15 августа 2011

Класс Policy в java 6 содержит статическую ссылку на загрузчик классов, который инициализируется для загрузчика классов текущего потока в контексте при первом доступе к классу:

private static ClassLoader contextClassLoader;

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

Слушатель жизненного цикла Tomcats обязательно инициализирует этот класс из известной среды, в которой для загрузчика классов контекста установлено значение системного загрузчика классов. Если к этому классу впервые был получен доступ из веб-приложения, он сохранил бы ссылку на загрузчик классов веб-приложений. Это предотвратит сборку мусора классами webapps, что приведет к утечке пространства perm gen.

...