Какая реализация лучше: кеш на основе WeakHashMap или кеш на ThreadLocal? - PullRequest
3 голосов
/ 29 июня 2010

Мне трудно выбрать между следующими двумя реализациями.Я хочу кэшировать объект javax.xml.parsers.DocumentBuilder для каждого потока.Моя главная забота - производительность во время выполнения - Хенч, я был бы рад избежать как можно большего количества GC.Память не проблема.

Я написал две реализации POC, и был бы рад услышать от PROS / CONS сообщества относительно каждой из них.

Спасибо за помощь, ребята.

Опция # 1 - WeakHashMap

import java.io.IOException;
import java.io.StringReader;
import java.util.WeakHashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class DocumentBuilder_WeakHashMap {
    private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private static final WeakHashMap<Thread, DocumentBuilder> CACHE = new WeakHashMap<Thread, DocumentBuilder>();

    public static Document documentFromXMLString(String xml) throws SAXException, IOException, ParserConfigurationException {
        DocumentBuilder builder = CACHE.get(Thread.currentThread());
        if(builder == null) {
            builder = factory.newDocumentBuilder();
            CACHE.put(Thread.currentThread(), builder);
        }

        return builder.parse(new InputSource(new StringReader(xml)));
    }

}

Опция # 2 - ThreadLocal

import java.io.IOException;
import java.io.StringReader;
import java.lang.ref.WeakReference;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class DocumentBuilder_ThreadLocal {
    private static final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    private static final ThreadLocal<WeakReference<DocumentBuilder>> CACHE = 
        new ThreadLocal<WeakReference<DocumentBuilder>>() {
            @Override 
            protected WeakReference<DocumentBuilder> initialValue() {
                try {
                    return new WeakReference<DocumentBuilder>(factory.newDocumentBuilder());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        };

    public static Document documentFromXMLString(String xml) throws ParserConfigurationException, SAXException, IOException {
        WeakReference<DocumentBuilder> builderWeakReference = CACHE.get();
        DocumentBuilder builder = builderWeakReference.get();

        if(builder == null) {
            builder = factory.newDocumentBuilder();
            CACHE.set(new WeakReference<DocumentBuilder>(builder));
        }

        return builder.parse(new InputSource(new StringReader(xml)));
    }
}

Они оба делают одно и то же (открывают documentFromXMLString () для внешнего мира), так чтокого бы вы использовали?

Спасибо, Максим.

Ответы [ 3 ]

6 голосов
/ 29 июня 2010

Решение ThreadLocal лучше, если вы не используете слабую ссылку, а используете непосредственно ThreadLocal<DocumentBuilder>. Доступ к значению ThreadLocal быстрее, потому что поток напрямую ссылается на массив, содержащий все значения ThreadLocal, и ему просто нужно вычислить индекс в этом массиве для поиска. Посмотрите на ThreadLocal source , чтобы понять, почему вычисление индекса происходит быстро (int index = hash & values.mask;)

4 голосов
/ 22 марта 2011

BEWARE!

ThreadLocal сохранит неопределенную ссылку на DocumentBuilder, которая содержит ссылку на последние XML-документы, проанализированные этим потоком DocumentBuilder.

Это имеет несколько последствий, которые можно считать утечками памяти:

  • Если реализация JAXP загружена в веб-приложение (скажем, Xerces или Oracle xmlparser2.jar), эта сохраненная ссылка на DocumentBuilder приведет к утечке всех классов вашего веб-приложения при отмене развертывания, что в итоге приведет к OutOfMemoryError: PermGenSpace! (Google вокруг для получения дополнительной информации по этой теме)
  • Если последний XML-документ, проанализированный DocumentBuilder, имеет большой размер, он будет занимать память до тех пор, пока новый XML-документ не будет проанализирован в этом потоке. Если в пуле потоков есть долго работающие потоки (например, в контейнере J2EE), это может быть проблемой, особенно если необходимо проанализировать много больших документов. Да, в конечном итоге память будет освобождена, но вам может не хватить используемой памяти до того, как это произойдет, и GC не сможет очистить XML-документ, пока существует ссылка на DocumentBuilder.

Решите, имеет ли это отношение к вам или нет ...

3 голосов
/ 29 июня 2010

Один WeakHashMap потерпит неудачу, потому что он не безопасен для потоков:
"Как и большинство классов коллекций, этот класс не синхронизирован."
(3-й абзац в JavaDoc )

Поскольку синхронизация займет время, а Collections.synchronizedMap не очень хорошо масштабируется, вам следует придерживаться ThreadLocal.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...