The Kryo Serializer is consuming more space than the default serializer in EHCache

53 Views Asked by At

I am attempting to save an Employee object to the off-heap tier of the EHCache using the Kryo serializer, and it is occupying more space than when I attempted to save it without the Kryo serializer. I have provided the code snippet and tier statistics below. Any suggestion why is it behaving like this?

Tier Statistic with Kryo

tierStats=[TierStats(tierName=OffHeap, allocatedByteSize=6094848, occupiedByteSize=4184000, evictions=0, expirations=0, hits=0, misses=0, mappings=1000, puts=0, removals=0)])

Tier Statistic without Kryo

tierStats=[TierStats(tierName=OffHeap, allocatedByteSize=4259840, occupiedByteSize=119200, evictions=0, expirations=0, hits=0, misses=0, mappings=1000, puts=0, removals=0)])

Also I used Kryo to serialize same set of objects and saved in the fie, and the file size is around 4KB which is way less than occupiedByteSize in EHCache

version

    implementation 'org.ehcache:ehcache:3.10.0'
    implementation 'com.esotericsoftware:kryo:5.6.0'

Code Snippet

public class Employee implements Serializable {
    String name;
    int id;
    public Employee(String name, int id) {
        this.name = name;
        this.id = id;
    }
}

public class EmployeeKryoSerializer implements Serializer<Employee> {
    private static final Kryo kryo = new Kryo();
    public EmployeeKryoSerializer(ClassLoader loader) {
        kryo.register(Employee.class);
    }
    @Override
    public ByteBuffer serialize(Employee object) throws SerializerException {
        Output output = new Output(new ByteArrayOutputStream());
        kryo.writeObject(output, object);
        output.close();
        return ByteBuffer.wrap(output.getBuffer());
    }
    @Override
    public Employee read(ByteBuffer binary) throws SerializerException {
        Input input = new Input(new ByteBufferInputStream(binary));
        return kryo.readObject(input, Employee.class);
    }
    @Override
    public boolean equals(Employee object, ByteBuffer binary) throws ClassNotFoundException, SerializerException {
        return object.equals(read(binary));
    }
}


public class KryoSerializationTest {

    public static void main(String[] args) throws IOException {
        Employee test = new Employee("TestName", 1);
        testNormalKryoDummyObject(test);
        testEHCacheDummyObject(test);
    }
    private static void testNormalKryoDummyObject( Employee test) throws IOException {
        Kryo kryo = new Kryo();
        kryo.register(Employee.class);
        Output output = new Output(new ByteArrayOutputStream());
        for (int i = 0; i < 1000; i++) {
            kryo.writeObject(output, test);
        }
        output.close();
        FileOutputStream fileOutputStream = new FileOutputStream("kryo_employee.bin");
        fileOutputStream.write(output.getBuffer());
        fileOutputStream.flush();
        fileOutputStream.close();
    }

    private static void testEHCacheDummyObject(Employee test){
        StatisticsService odStatisticsService = new DefaultStatisticsService();
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
            .using(odStatisticsService)
            .build(true);
        Cache<String, Employee> odPairCache = cacheManager.createCache("TEST_EH_CACHE",
            CacheConfigurationBuilder.newCacheConfigurationBuilder(
                    String.class,
                    Employee.class,
                    ResourcePoolsBuilder.newResourcePoolsBuilder().offheap(100, MemoryUnit.MB).build())
                .withValueSerializer(EmployeeKryoSerializer.class)
                .build());
        for (int i = 0; i < 1000; i++) {
            odPairCache.put("XYZ"+i+"-"+"ABC",test);
            printEHStatistic("TEST_EH_CACHE", odStatisticsService);
        }
    }
    private static void printEHStatistic(String cacheName,  StatisticsService odStatisticsService) {
        CacheStatistics cacheStatistics = odStatisticsService.getCacheStatistics(cacheName);
        EHCacheStatistic ehCacheStatistic =
            EHCacheStatistic.builder()
                .cacheName(cacheName)
                .cacheMissPercentage(cacheStatistics.getCacheMissPercentage())
                .cacheEvictions(cacheStatistics.getCacheEvictions())
                .cacheExpirations(cacheStatistics.getCacheExpirations())
                .cacheGets(cacheStatistics.getCacheGets())
                .cacheMisses(cacheStatistics.getCacheMisses())
                .cacheRemovals(cacheStatistics.getCacheRemovals())
                .cachePuts(cacheStatistics.getCachePuts())
                .cacheHits(cacheStatistics.getCacheHits())
                .cacheHitPercentage(cacheStatistics.getCacheHitPercentage())
                .tierStats(new ArrayList<>())
                .build();
        cacheStatistics
            .getTierStatistics()
            .forEach(
                (tierName, tierStats) -> ehCacheStatistic
                    .getTierStats()
                    .add(
                        TierStats.builder()
                            .tierName(tierName)
                            .allocatedByteSize(tierStats.getAllocatedByteSize())
                            .occupiedByteSize(tierStats.getOccupiedByteSize())
                            .evictions(tierStats.getEvictions())
                            .expirations(tierStats.getExpirations())
                            .hits(tierStats.getHits())
                            .misses(tierStats.getMisses())
                            .mappings(tierStats.getMappings())
                            .puts(tierStats.getPuts())
                            .removals(tierStats.getRemovals())
                            .build()));
        System.out.println(ehCacheStatistic.toString());
    }

}

Need to know why EHcache taking more space with Kryo Serializer

1

There are 1 best solutions below

1
Chris Dennis On

I can't speak for what Kryo is doing but the default serializer in Ehcache is giving you:

ACED     j.i.ObjectStreamConstants.STREAM_MAGIC
0005     j.i.ObjectStreamConstants.STREAM_VERSION
73       j.i.ObjectStreamConstants.TC_OBJECT
72       j.i.ObjectStreamConstants.TC_CLASSDESC
00000000 <class index = 0>
78       j.i.ObjectStreamConstants.TC_ENDBLOCKDATA
70       j.i.ObjectStreamConstants.TC_NULL
00000001 <value of int id>
74       j.i.ObjectStreamConstants.TC_STRING
0008     <8 UTF characters>
546573744E616D65 == TestName

That's 27 bytes in total. Default Ehcache serialization is pretty space efficient. I'm sure with enough configuration you could get Kryo underneath this. That would be an exercise for someone more experienced with Kyro though.