Сохраняются ли Interned Strings при сериализации? - PullRequest
3 голосов
/ 21 октября 2011

Если у меня большой граф объектов, который содержит много повторяющихся строк, есть ли преимущество для интернирования () строк перед их сериализацией? Это уменьшит объем передаваемых данных? Будут ли строки совместно использовать указатели на принимающей стороне?

Я предполагаю, что строки будут де-дублированы перед отправкой, что уменьшит размер данных и что все они будут представлены одним и тем же объектом на принимающей стороне, но на самом деле они не будут интернированы на получающий конец. (имеется в виду, что в каждой транзакции сериализации будет создан один новый экземпляр строки)

Ответы [ 4 ]

6 голосов
/ 21 октября 2011

Достаточно просто проверить:

import java.io.*;

class Foo implements Serializable {
    private String x;
    private String y;

    public Foo(String x, String y) {
        this.x = x;
        this.y = y;
    }
}

public class Test {
    public static void main(String[] args) throws IOException {
        String x = new StringBuilder("hello").append(" world").toString();
        String y = "hello world";

        showSerializedSize(new Foo(x, y));
        showSerializedSize(new Foo(x, x));
    }

    private static void showSerializedSize(Foo foo) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(foo);
        oos.close();
        System.out.println(baos.size());
    }
}

Результаты на моей машине:

86
77

Таким образом, похоже, что дедупликация не не происходит автоматически.

Я бы не стал использовать String.intern() сам, так как вы, вероятно, не хотите, чтобы все эти строки были в обычном пуле для интернов, но вы всегда можете использовать HashSet<String> для создания «временного» интернабассейн.

4 голосов
/ 22 октября 2011

ObjectOutputStream отслеживает граф объекта (до сброса), один объект записывается только один раз, даже если он достигнут несколькими ссылками. Сокращение объектов путем интернирования определенно уменьшит байты.

На принимающей стороне воссоздается тот же граф объектов, поэтому один экземпляр строки на отправляющей стороне становится одним экземпляром строки на принимающей стороне.

2 голосов
/ 22 октября 2011

Вы можете использовать это расширение ObjectOutputStream, которое реализует дедупликацию String.Вывод должен быть совместим с исходной версией (не тестировался), поэтому никаких специальных ObjectInputStream не требуется.

Обратите внимание, что используется не String.intern(), а личный и временный внутренний Map, поэтому вашPermGenSpace не затоплен.

public class StringPooledObjectOutputStream extends ObjectOutputStream {
    private Map<String, String> stringPool = new HashMap<String, String>();
    public StringPooledObjectOutputStream(OutputStream out) throws IOException {
        super(out);
        enableReplaceObject(true);
    }

    @Override
    protected Object replaceObject(Object obj) throws IOException {
        if( !(obj instanceof String) )
            return super.replaceObject(obj);

        String str = (String)obj;

        String replacedStr = stringPool.get(str);
        if( replacedStr == null ){
            replacedStr = (String)super.replaceObject(str);
            stringPool.put(replacedStr, replacedStr);
        }
        return replacedStr;
    }
}
0 голосов
/ 22 октября 2011

Перед сериализацией, похоже, нет смысла интернировать строки.По крайней мере, это ничего не изменит для сериализации.Это может помочь уменьшить память вашего приложения.

На принимающей стороне будет вызываться самый низкий уровень readUTF() из ObjectOutPutStream или его эквивалент, который будет выделять новую строку для каждого вызова.Если ваш класс доступен для внешнего использования, вы можете сделать readUTF().intern(), чтобы сохранить память на стороне получателя.Я сам использовал этот метод и получил более чем 50% -ное сокращение использования памяти клиентским приложением.

Однако учтите, что если имеется много уникальных строк, то intern() может вызвать проблему нехватки памяти, поскольку она использует PermGen,См .: http://www.onkarjoshi.com/blog/213/6-things-to-remember-about-saving-memory-with-the-string-intern-method/

Я только интернировал строки, которые были меньше 10 символов и не столкнулись с какой-либо проблемой.

...