Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 8558883
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 11, 20262026-06-11T15:54:49+00:00 2026-06-11T15:54:49+00:00

As I understand, Kryo creates a className <-> numberID map in every writeObject .

  • 0

As I understand, Kryo creates a className<->numberID map in every writeObject. This map is too narrow. Because in your object model, instances tend to belong to the same classes, next writeObject will build and serialize a similar map again (and again, and again, and again). I know that the map can be shared by registering the classes manually but this is tedious manual hardcoding. I would like the map would be started by the first object write, as it does normally, but all subsequent writes in the session would reuse and extend it. This way, registration would occur automatically at runtime, with no additional runtime overhead and more frequently used objects would naturally receive the low id numbers. The map can be stored afterwards, separately, in attachment, as a decryption key. Deserializer will start by loading this map. How do you like the idea, and how can it be implemented?

My question is similar to this
Strategy for registering classes with kryo where user could combine all writes under single writeObject using a List. It would be even simpler than storing a map separately, as I propose. But, it seems he does not wish to do so. In my case, such combination is not even possible because of large java model I avoid keeping it wholly in memory by serializing in pieces. In my scenario, user opens a project, makes changes and flushes them. So, the project could maintain a map of classes and use it for all serializations.

Update! I have realized that there are class/object registrators and autoReset. They seem to be created right for the task. However, I do not see how these things solve it. Autoreset=false does make the second write much smaller indeed. However, I fail to deserialize objects in this case. As you see in example, second deserialization fails:

public class A {
    String f1;
    A(String a) {
        f1 = a;
    }
    List list = new ArrayList();
    public String toString() {
        return "f1 = " + f1 + ":" + f1.getClass().getSimpleName();
    }

    public static void main(String[] args) {
        test(true);
        test(false);
    }


    static void write(String time, Kryo kryo, ByteArrayOutputStream baos, Object o) {
        Output output = new Output(baos); 
        kryo.writeClassAndObject(output, o); 
        output.close();
        System.err.println(baos.size() + " after " + time + " write");
    }

    private static void test(boolean autoReset) {
        Kryo kryo = new Kryo();
        kryo.setAutoReset(autoReset);
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
        System.err.println("-------\ntesting autoreset = " + autoReset);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        A a = new A("a"), b = new A("b");
        write("first", kryo, baos, a);
        write("second", kryo, baos, b);
        A o1 = restore("first", baos, kryo);
        A o2 = restore("second", baos, kryo); // this fails
        System.err.println((o1.f1.equals(o2.f1)) ? "SUCCESS" : "FAILURE");

    }

    private static A restore(String time, ByteArrayOutputStream baos, Kryo k) {
        ByteArrayInputStream in = new ByteArrayInputStream(baos.toByteArray());
        Input input = new Input(in);
        A o = (A) k.readClassAndObject(input);
        System.err.println("reading object " + time + " time, got " + o);
        return o;
    }

Output is

-------
testing autoreset = true
41 after first write
82 after second write
reading object first time, got f1 = a:String
reading object second time, got f1 = a:String
SUCCESS
-------
testing autoreset = false
41 after first write
52 after second write
reading object first time, got f1 = a:String
reading object second time, got null
Exception in thread "main" java.lang.NullPointerException
    at kryo_test.AutoresetDemo.test(AutoresetDemo.java:40)
    at kryo_test.AutoresetDemo.main(AutoresetDemo.java:18)

Update2 It may also happen that autoReset=false records object references in addition to class names. It is worth to autoreset indeed.

Update3 I have discovered that it is difficult to serialize the map of classes (i.e., class -> registration) because registrations contain serializers that refer a kryo object and keep some state. It is difficult to share the map between many kryo objects then.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-11T15:54:50+00:00Added an answer on June 11, 2026 at 3:54 pm

    Ok, here is a solution for kryo-2.20

    public class GlobalClassKryo extends Kryo {
    
        public static class ExternalizableClassResolver implements ClassResolver {
    
            //local serializers
            final Map<Class, Registration> fromClass = new HashMap();
            final Map<Integer, Registration> fromId = new HashMap();
    
            public static class GlobalRegistration { int id; Class type; Class<? extends Serializer> serializer; }
    
            public final Map<Integer, GlobalRegistration> globalIds; 
            public final Map<Class, GlobalRegistration> globalClasses; 
    
            // I synchronize because I have one reader and one writer thread and 
            // writer may break the reader when adds something into the map. 
            public ExternalizableClassResolver() {this (
                    Collections.synchronizedMap(new HashMap()), 
                    Collections.synchronizedMap(new HashMap())
                ) ;}
    
            public ExternalizableClassResolver(Map<Integer, GlobalRegistration> ids, Map<Class, GlobalRegistration> classes) {
                globalIds = ids;
                globalClasses = classes;
            }
    
            public ExternalizableClassResolver (DataInput in) throws ClassNotFoundException, IOException {
                this();
                int id;
                while ((id = in.readInt()) != -1) {
                    GlobalRegistration e = new GlobalRegistration();
                    globalIds.put(e.id = id, e);
                    e.type = Class.forName(in.readUTF());
                    e.serializer = (Class<? extends Serializer>) Class.forName(in.readUTF());
                    globalClasses.put(e.type, e);
                }
            }
    
            public void save(DataOutput out) throws IOException {
                for (GlobalRegistration entry : globalIds.values()) {
                        out.writeInt(entry.id);
                        out.writeUTF(entry.type.getName());
                        out.writeUTF(entry.serializer.getName());
                }
                out.writeInt(-1);
            }
    
            static final boolean TRACE = false;
            void log(String msg) {
                System.err.println(kryo != null ? Utils.fill(kryo.getDepth(), ' ')  + msg : msg);
            }
            @Override
            public Registration writeClass(Output output, Class type) {
                if (type == null) {output.writeInt(0, true); return null;}
                Registration registration = kryo.getRegistration(type);
                output.writeInt(registration.getId(), true);
                return registration;
            }
            @Override
            public Registration readClass(Input input) {
                int classID = input.readInt(true);
                if (classID == 0) return null;
                Registration registration = fromId.get(classID);
                if (registration == null) {
                    registration = tryGetFromGlobal(globalIds.get(classID), classID + "");
                }
                if (registration == null) throw new KryoException("Encountered unregistered class ID: " + classID);
                return registration;
            }
    
            public Registration register(Registration registration) {
                throw new KryoException("register(registration) is not allowed. Use register(type, serializer)");
            }
    
            public Registration getRegistration(int classID) {
                throw new KryoException("getRegistration(id) is not implemented");
            }
    
            Registration tryGetFromGlobal(GlobalRegistration globalClass, String title) {
                if (globalClass != null) {
                    Serializer serializer = kryo.newSerializer(globalClass.serializer, globalClass.type);
                    Registration registration = register(globalClass.type, serializer, globalClass.id, "local");
                    if (TRACE) log("getRegistration(" + title + ") taken from global => " + registration);
                    return registration;
                } else
                    if (TRACE) log("getRegistration(" + title + ") was not found");
                return null;
            }
            public Registration getRegistration(Class type) {
                Registration registration = fromClass.get(type);
                if (registration == null) {
                    registration = tryGetFromGlobal(globalClasses.get(type), type.getSimpleName());
                } else
                    if (TRACE) log("getRegistration(" + type.getSimpleName() + ") => " + registration);
    
                return registration;
            }
    
            Registration register(Class type, Serializer serializer, int id, String title) {
                Registration registration = new Registration(type, serializer, id);
                fromClass.put(type, registration);
                fromId.put(id, registration);
    
                if (TRACE) log("new " + title + " registration, " + registration);
    
                //why dont' we put into fromId?
                if (registration.getType().isPrimitive()) fromClass.put(getWrapperClass(registration.getType()), registration);
                return registration;
            }
    
            int primitiveCounter = 1; // 0 is reserved for NULL
            static final int PRIMITIVE_MAX = 20;
    
            //here we register anything that is missing in the global map. 
            // It must not be the case that something available is registered for the second time, particularly because we do not check this here
            // and use registered map size as identity counter. Normally, check is done prior to callig this method, in getRegistration
            public Registration register(Class type, Serializer serializer) {
    
                if (type.isPrimitive() || type.equals(String.class))
                    return register(type, serializer, primitiveCounter++, "primitive");
    
                GlobalRegistration global = globalClasses.get(type);
    
                if (global != null )
                        throw new RuntimeException("register(type,serializer): we have " + type + " in the global map, this method must not be called");
    
                global = new GlobalRegistration();
                globalIds.put(global.id = globalClasses.size() + PRIMITIVE_MAX, global); 
                globalClasses.put(global.type = type, global);
                global.serializer= serializer.getClass();
    
                return register(global.type, serializer, global.id, "global");
            }
    
            public Registration registerImplicit(Class type) {
                throw new RuntimeException("registerImplicit is not needed since we register missing automanically in getRegistration");
            }
    
            @Override
            public void reset() {
                // super.reset(); //no need to reset the classes
            }
    
            Kryo kryo;
            public void setKryo(Kryo kryo) {
                this.kryo = kryo;
            }
        }
    
    
    
    
        public ExternalizableClassResolver ourClassResolver() {
            return (ExternalizableClassResolver) classResolver;
        }
    
        public GlobalClassKryo(ClassResolver resolver) {
            super(resolver, new MapReferenceResolver()); 
            setInstantiatorStrategy(new StdInstantiatorStrategy());
            this.setRegistrationRequired(true);
        }
        public GlobalClassKryo() {
            this(new ExternalizableClassResolver());
        }
    
        @Override
        public Registration getRegistration (Class type) {
            if (type == null) throw new IllegalArgumentException("type cannot be null.");
    
            if (type == memoizedClass) return memoizedClassValue;
            Registration registration = classResolver.getRegistration(type);
            if (registration == null) {
                if (Proxy.isProxyClass(type)) {
                    // If a Proxy class, treat it like an InvocationHandler because the concrete class for a proxy is generated.
                    registration = getRegistration(InvocationHandler.class);
                } else if (!type.isEnum() && Enum.class.isAssignableFrom(type)) {
                    // This handles an enum value that is an inner class. Eg: enum A {b{}};
                    registration = getRegistration(type.getEnclosingClass());
                } else if (EnumSet.class.isAssignableFrom(type)) {
                    registration = classResolver.getRegistration(EnumSet.class);
                }
                if (registration == null) {
                    //registration = classResolver.registerImplicit(type);
                    return register(type, getDefaultSerializer(type));
                }
            }
            memoizedClass = type;
            memoizedClassValue = registration;
            return registration;
        }
    
        public Registration register(Class type, Serializer serializer) {
            return ourClassResolver().register(type, serializer);}
    
        public Registration register(Registration registration) {
            throw new RuntimeException("only register(Class, Serializer) is allowed");}
    
        public Registration register(Class type) {
            throw new RuntimeException("only register(Class, Serializer) is allowed");}
    
        public Registration register(Class type, int id) {
            throw new RuntimeException("only register(Class, Serializer) is allowed");}
    
        public Registration register(Class type, Serializer serializer, int id) {
            throw new RuntimeException("only register(Class, Serializer) is allowed");
        }
    
        static void write(String title, Kryo k, ByteArrayOutputStream baos, Object obj) {
            Output output = new Output(baos);
            k.writeClassAndObject(output, obj);
            output.close();
            System.err.println(baos.size() + " bytes after " + title + " write");
        }
        static class A {
            String field = "abcABC";
            A a = this;
            //int b = 1; // adds 1 byte to serialization
            @Override
            public String toString() {
                return field 
                        + " " + list.size() 
                        //+ ", " + b
                        ;
            }
    
            // list adds 3 bytes to serialization, two 3-byte string items add additionally 10 bytes in total
            ArrayList list = new ArrayList(100); // capacity is trimmed in serialization
            {
                list.add("LLL");
                list.add("TTT");
    
            }
        }
    
        private static void test() throws IOException, ClassNotFoundException {
            GlobalClassKryo k = new GlobalClassKryo();
    
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
    
            write("first", k, baos, new A()); // write takes 24 byts
    
            //externalize the map
            ByteArrayOutputStream mapOut = new ByteArrayOutputStream();
            DataOutputStream dataOut = new DataOutputStream(mapOut);
            k.ourClassResolver().save(dataOut);
            dataOut.close();
    
            //deserizalize the map
            DataInputStream serialized = new DataInputStream(new ByteArrayInputStream(mapOut.toByteArray()));
            ExternalizableClassResolver resolver2 = new ExternalizableClassResolver(serialized);
    
            //use the map
            k = new GlobalClassKryo(resolver2);
            write("second", k, baos, new A()); // 24 bytes
    
            Input input = new Input(new ByteArrayInputStream(baos.toByteArray()));
            Object read = k.readClassAndObject(input);
            System.err.println("output " + read);
        }
    
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            Kryo k = new Kryo();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            write("first", k, baos, new A()); // write takes 78 bytes
            write("second", k, baos, new A()); // +78 bytes
            System.err.println("----------------");
    
            test();
        }
    }
    

    Resulting stream is clean of class names. Unfortunately, Kryo turns out to be too slow compared to default java serialization (2x or more), though it’s resulting stream is much more dense. Kryo alone makes my sample serializations almost 10x smaller. You see that the solution presented in this answer adds additional factor of 3x. But, in the fields, where I serialize megabytes, I am getting only 2x compression with respect to java serialization and 2x slowdown when storing to disk with Kryo.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I understand that @synthesize window; combined with @property 'auto-creates' your setters and getters, but
I understand this is an easy question but for some reason this just isn't
I understand I can create an enum like this: public enum MyEnum { ONE(1),
I understand that there is a *.className selector since there can be multiple html
I understand this might be a very basic question. Windows Azure needs me to
I understand using constants for your names in a NSDictionary to prevent typos (myName
I understand the following Python code: >>> class A(object): ... def __str__(self): ... return
I understand that this a very noob problem, but when I try to create
I understand that this may not necessarily apply to just @properties, but they would
I understand that this is a bad idea for lots of scenarios. I am

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.