You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ChronicleMap自定义Key/Value序列化器实现及栈溢出问题求助

ChronicleMap自定义Key/Value序列化问题及栈溢出错误解决

我正在尝试用ChronicleMap存储自定义Key和Value:Key基于基类CacheKey(包含NetKey等多个实现类),Value基于基类CacheValue(比如NetValue),目的是将数据库查询结果存入内存以加速后续请求,但卡在了序列化器实现上,还出现了栈溢出错误。

现有代码实现

基类定义

public interface CacheKey extends Serializable {
    byte[] getKey();
    void setKey(Object object);
}

public interface CacheValue extends Serializable {
    byte[] getValue();
    void setValue(Object object);
}

Key实现类

@Builder
public class NetKey implements CacheKey {

    private String name;
    private String productType;
    private String dealDate;
    
    @Override
    public byte[] getKey() {
        return Util.writeToByte(this);
    }

    @Override
    public void setKey(Object object) {
        if (object instanceof NetKey) {
            NetKey o = (NetKey) object;
            this.name = o.name;
            this.productType = o.productType;
            this.dealDate = o.dealDate;
        }
    }

    // 已重写equals和hashCode
}

Value实现类

@Builder
public class NetValue implements CacheValue {

    private String entity;
    private String group;
    private Service service;

    @Override
    public byte[] getValue() {
        return Util.writeToByte(this);
    }

    @Override
    public void setValue(Object object) {
        if (object instanceof NetValue) {
            NetValue o = (NetValue) object;
            this.entity = o.entity;
            this.group = o.group;
            this.service = o.service;
        }
    }
 
    // 已重写equals和hashCode
}

序列化器实现

public class MyKeySerializer implements BytesReader<CacheKey>, BytesWriter<CacheKey> {

    private static MyKeySerializer INSTANCE = new MyKeySerializer();

    public static MyKeySerializer getInstance() { return INSTANCE; }

    private MyKeySerializer() {}

    @NotNull
    @Override
    public CacheKey read(Bytes in, @Nullable CacheKey using) {
        if (using == null) {
            using = new CacheKey() {
                @Override
                public CacheKey getKey() {
                    return this;
                }

                @Override
                public void setKey(CacheKey object) {

                }
            };
        }

        int len = in.readInt();
        Object object = Util.readFromByte(in.toByteArray());
        using.setKey(object == null ? null : (CacheKey) object);
        return using;
    }

    @Override
    public void write(Bytes out, @NotNull CacheKey toWrite) {
        byte[] content = Util.writeToByte(toWrite.getKey());
        out.writeInt(content.length);
        out.write(content);
    }

}

public class MyValueSerializer implements BytesReader<CacheValue>, BytesWriter<CacheValue> {

    private static MyValueSerializer INSTANCE = new MyValueSerializer();

    public static MyValueSerializer getInstance() { return INSTANCE; }

    private MyValueSerializer() {}

    @NotNull
    @Override
    public CacheValue read(Bytes in, @Nullable CacheValue using) {
        // TODO 需要实现该方法
    }

    @Override
    public void write(Bytes out, @NotNull CacheValue toWrite) {
        // TODO 需要实现该方法
    }

}

测试代码

public class Test {
    
    public static void main(String[] args) {
        NetKey netKey = ...;
        NetValue netValue = ...;

        ChronicleMap<CacheKey, CacheValue> inMemoryMap = ChronicleMap.of(CacheKey.class, CacheKey.class)
           .name("sample-map")
           .entries(50)
           .averageKey(netKey)
           .averageValue(netValue)
           .keyMarshallers(MyKeySerializer.class)
           .valueMarshallers(MyValueSerializer.class)
           .create();
        inMemoryMap.put(netKey, netValue);
    }
}

错误信息(更新1)

出现栈溢出错误:

Exception in thread "main" java.lang.StackOverflowError
    at java.base/java.lang.RuntimeException.<init>(RuntimeException.java:52)
    at java.base/java.lang.IllegalArgumentException.<init>(IllegalArgumentException.java:40)
    at java.base/java.util.regex.PatternSyntaxException.<init>(PatternSyntaxException.java:58)
    at java.base/java.util.regex.Pattern.error(Pattern.java:2028)
    at java.base/java.util.regex.Pattern.<init>(Pattern.java:1432)
    at java.base/java.util.regex.Pattern.compile(Pattern.java:1069)
    at java.base/java.util.regex.Pattern.matches(Pattern.java:1174)
    at java.base/java.lang.String.matches(String.java:2839)
    at net.openhft.chronicle.values.CodeTemplate.lambda$null$20(CodeTemplate.java:206)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:178)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
    at java.base/java.util.TreeMap$KeySpliterator.tryAdvance(TreeMap.java:3088)
    at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
    at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
    at net.openhft.chronicle.values.CodeTemplate.lambda$methodsAndTemplatesByField$21(CodeTemplate.java:207)
    at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
    at net.openhft.chronicle.values.CodeTemplate.methodsAndTemplatesByField(CodeTemplate.java:214)
    at net.openhft.chronicle.values.CodeTemplate.createValueModel(CodeTemplate.java:104)
    at net.openhft.chronicle.values.ValueModel$1.computeValue(ValueModel.java:46)
    at java.base/java.lang.ClassValue.getFromHashMap(ClassValue.java:228)
    at java.base/java.lang.ClassValue.getFromBackup(ClassValue.java:210)

问题分析与解决方案

1. 栈溢出错误根源

测试代码中创建ChronicleMap时,泛型参数写错:ChronicleMap.of(CacheKey.class, CacheKey.class),第二个参数应该是CacheValue.class。这个错误会导致ChronicleMap内部代码生成逻辑陷入循环,最终触发栈溢出。

2. 序列化器逻辑修正

现有序列化器存在循环序列化、数据读取错误等问题,以下是修正后的实现:

修正后的MyKeySerializer
public class MyKeySerializer implements BytesReader<CacheKey>, BytesWriter<CacheKey> {

    private static final MyKeySerializer INSTANCE = new MyKeySerializer();

    public static MyKeySerializer getInstance() { return INSTANCE; }

    private MyKeySerializer() {}

    @NotNull
    @Override
    public CacheKey read(Bytes in, @Nullable CacheKey using) {
        // 读取类型标识,区分不同CacheKey实现类
        int type = in.readInt();
        byte[] data = new byte[in.readInt()];
        in.read(data);
        
        Object obj = Util.readFromByte(data);
        if (using != null) {
            using.setKey(obj);
            return using;
        }
        return (CacheKey) obj;
    }

    @Override
    public void write(Bytes out, @NotNull CacheKey toWrite) {
        // 写入类型标识,NetKey用1,其他实现类可定义不同值
        if (toWrite instanceof NetKey) {
            out.writeInt(1);
        } else {
            out.writeInt(0); // 默认类型标识
        }
        // 直接序列化对象本身,避免循环调用getKey
        byte[] content = Util.writeToByte(toWrite);
        out.writeInt(content.length);
        out.write(content);
    }
}
实现完整的MyValueSerializer
public class MyValueSerializer implements BytesReader<CacheValue>, BytesWriter<CacheValue> {

    private static final MyValueSerializer INSTANCE = new MyValueSerializer();

    public static MyValueSerializer getInstance() { return INSTANCE; }

    private MyValueSerializer() {}

    @NotNull
    @Override
    public CacheValue read(Bytes in, @Nullable CacheValue using) {
        int type = in.readInt();
        byte[] data = new byte[in.readInt()];
        in.read(data);
        
        Object obj = Util.readFromByte(data);
        if (using != null) {
            using.setValue(obj);
            return using;
        }
        return (CacheValue) obj;
    }

    @Override
    public void write(Bytes out, @NotNull CacheValue toWrite) {
        if (toWrite instanceof NetValue) {
            out.writeInt(1);
        } else {
            out.writeInt(0);
        }
        byte[] content = Util.writeToByte(toWrite);
        out.writeInt(content.length);
        out.write(content);
    }
}

3. 修正测试代码

public class Test {
    
    public static void main(String[] args) {
        NetKey netKey = NetKey.builder().name("test").productType("type1").dealDate("20240101").build();
        NetValue netValue = NetValue.builder().entity("entity1").group("group1").service(new Service()).build();

        ChronicleMap<CacheKey, CacheValue> inMemoryMap = ChronicleMap.of(CacheKey.class, CacheValue.class)
           .name("sample-map")
           .entries(50)
           .averageKey(netKey)
           .averageValue(netValue)
           .keyMarshaller(MyKeySerializer.getInstance())
           .valueMarshaller(MyValueSerializer.getInstance())
           .create();
        inMemoryMap.put(netKey, netValue);
    }
}

额外注意事项

  • 确保Util.writeToByte和Util.readFromByte基于Java序列化时,所有涉及的类(包括Service)都实现Serializable接口。
  • 若后续新增CacheKey/CacheValue实现类,需同步更新序列化器中的类型标识逻辑,避免反序列化时类型错误。

内容的提问来源于stack exchange,提问作者user2459396

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:57:06