为什么在threadlocal中使用序列化程序会导致kafka生产者中的内存泄漏?

时间:2019-02-08 21:30:29

标签: java multithreading apache-kafka thread-safety

请考虑以下设置

prop.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, ThriftSerializer.class.getName());


public class ThriftSerializer implements Serializer<TBase> {
    private final ThreadLocal<TSerializer> serializer = new ThreadLocalTSerializer();

    @Override
    public void configure(Map map, boolean b) {

    }

    @Override
    public byte[] serialize(String s, TBase event) {
        try {
            return serializer.get().serialize(event);
        } catch (TException e) {
            return new byte[0];
        }
    }

    @Override
    public void close() {

    }
}

以上代码导致内存泄漏

但是我不明白为什么会发生。卡夫卡生产者会创建很多不会死的线程吗?

如果上面的代码替换为

@Override
public byte[] serialize(String s, TBase event) {
    TSerializer serializer = new TSerializer();

    try {
        return serializer.serialize(event);
    } catch (TException e) {
        return new byte[0];
    }
}

然后内存泄漏消失了,这很有意义,但是对于每个事件,它创建的新对象都需要进行垃圾收集,如果吞吐量很高,则可能导致gc压力

有人可以为我指出理解这种行为的方向吗?

1 个答案:

答案 0 :(得分:0)

据我所知,KafkaProducer是线程安全的,跨线程共享单个生产者实例通常比拥有多个实例要快。

但是send方法是异步的(除非不建议对send方法返回的Future对象调用.get(),建议不要这样做,否则您将等待每个发送并以同步方式对其进行处理)。

由于文档的缘故,生产者由一个缓冲空间池和一个I / O后台线程组成,该缓冲池保存尚未传输到服务器的记录,该I / O线程负责将这些记录转换为请求。并将其传输到集群。使用后如果无法关闭生产者,则会泄漏这些资源。

似乎send方法实际上使用了后台线程来转换您的记录并将其发送到集群。

您实际上是在最后关闭生产者吗?

    producer.flush();
    producer.close();

要关闭Kafka会话时,将调用序列化程序的close方法。 我的猜测是,您可以尝试使用序列化器的close方法进行一些其他清理,或将其标记为符合垃圾收集条件。