AntlrV4 的内存泄漏问题

/**
 * fix antlr memory leak
 * @see <a href="https://github.com/antlr/antlr4/issues/499"> Memory Leak </a>
 * @author victorchu
 * @date 2022/8/8 11:29
 */
import lombok.SneakyThrows;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.atn.ATN;
import org.antlr.v4.runtime.atn.LexerATNSimulator;
import org.antlr.v4.runtime.atn.ParserATNSimulator;
import org.antlr.v4.runtime.atn.PredictionContextCache;
import org.antlr.v4.runtime.dfa.DFA;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

public class RefreshableParserInitializer<L extends Lexer,P extends Parser> implements BiConsumer<L , P > {
    private final AtomicReference<ParserAndLexerATNCaches<L,P>> caches = new AtomicReference<>();

    public RefreshableParserInitializer() {
        Type superClass = this.getClass().getGenericSuperclass();
        lexer = (Class<?>)((ParameterizedType)superClass).getActualTypeArguments()[0];
        parser = (Class<?>)((ParameterizedType)superClass).getActualTypeArguments()[1];
        refresh();
    }

    public void refresh() {
        caches.set(buildATNCache());
    }

    // ================= 泛型处理 =====================
    private final Class<?> lexer;
    private final Class<?> parser;

    @SneakyThrows({SecurityException.class, NoSuchFieldException.class,IllegalAccessException.class})
    private static ATN getATNField(Class<?> clazz)  {
        Field field = clazz.getDeclaredField("_ATN");
        field.setAccessible(true);
        return (ATN)field.get(null);
    }

    private ParserAndLexerATNCaches<L,P> buildATNCache(){
        ATN lexerATN =getATNField(lexer);
        ATN parserATN = getATNField(parser);
        return new ParserAndLexerATNCaches(new AntlrATNCacheFields(lexerATN),new AntlrATNCacheFields(parserATN));
    }
    // ================= 泛型处理 =====================

    @Override
    public void accept(L l, P p) {
        ParserAndLexerATNCaches<L,P> cache =caches.get();
        cache.lexer.configureLexer(l);
        cache.parser.configureParser(p);
    }

    private static final class ParserAndLexerATNCaches<L extends Lexer,P extends Parser>
    {
        public ParserAndLexerATNCaches(AntlrATNCacheFields lexer, AntlrATNCacheFields parser) {
            this.lexer = lexer;
            this.parser = parser;
        }

        public final AntlrATNCacheFields lexer;
        public final AntlrATNCacheFields parser;
    }
    public static final class AntlrATNCacheFields
    {
        private final ATN atn;
        private final PredictionContextCache predictionContextCache;
        private final DFA[] decisionToDFA;

        public AntlrATNCacheFields(ATN atn)
        {
            this.atn = requireNonNull(atn, "atn is null");
            this.predictionContextCache = new PredictionContextCache();
            this.decisionToDFA = createDecisionToDFA(atn);
        }

        @SuppressWarnings("ObjectEquality")
        public void configureLexer(Lexer lexer)
        {
            requireNonNull(lexer, "lexer is null");
            // Intentional identity equals comparison
            checkArgument(atn == lexer.getATN(), "Lexer ATN mismatch: expected %s, found %s", atn, lexer.getATN());
            lexer.setInterpreter(new LexerATNSimulator(lexer, atn, decisionToDFA, predictionContextCache));
        }

        @SuppressWarnings("ObjectEquality")
        public void configureParser(Parser parser)
        {
            requireNonNull(parser, "parser is null");
            // Intentional identity equals comparison
            checkArgument(atn == parser.getATN(), "Parser ATN mismatch: expected %s, found %s", atn, parser.getATN());
            parser.setInterpreter(new ParserATNSimulator(parser, atn, decisionToDFA, predictionContextCache));
        }

        private static DFA[] createDecisionToDFA(ATN atn)
        {
            DFA[] decisionToDFA = new DFA[atn.getNumberOfDecisions()];
            for (int i = 0; i < decisionToDFA.length; i++) {
                decisionToDFA[i] = new DFA(atn.getDecisionState(i), i);
            }
            return decisionToDFA;
        }
    }
}

如何使用 RefreshableParserInitializer?

SqlBaseLexer baseLexer = xxx;
SqlBaseParser baseParser = xxx;

// 注意,此处需要构造一个匿名类
BiConsumer<SqlBaseLexer, SqlBaseParser> initializer = new RefreshableParserInitializer<SqlBaseLexer, SqlBaseParser>(){};
// refresh lexer和parser
initializer.accept(baseLexer,baseParser);

仅作为备份。原文链接:AntlrV4 的内存泄漏问题 | 代码之旅 (victorchu.info)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扰扰1994

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值