为什么 ANTLR4 解析器会累积 ATNConfig 对象?
Why ANTLR4 parsers accumulates ATNConfig objects?
在使用 ANTLR 为 C++ 开发解析器时,我们制作了一个批处理解析测试用例,其中构造了一个新的解析器来解析每个 C++ 源文件。开始时的性能是可以接受的——每个文件大约 15 秒。但是在解析了大约 150 多个文件之后,每个文件的解析时间越来越长,最后 jvm 抛出一个 "GC limit exceeded" 错误。
使用JProfiler我们发现在解析每个文件后,有很多ATNConfig对象被逐步积累。从大约 70M 开始,它们逐渐堆积到超过 500M,直到堆接近满并且 GC 占用 100% CPU 时间。 JProfiler 识别的最大对象(在堆中保留最多对象的对象)包括 DFA[] 和 PredictionContextCache。
需要注意的一件事是,我们使用了 2 个线程来 运行 并发解析任务。尽管线程不共享任何解析器或解析树对象,但我们注意到 ANTLR 生成的解析器正在使用静态字段,这可能会导致多线程设置中的内存问题。反正就是个嫌疑人。
有谁知道 "ATNConfig being accumulated" 的原因是什么?已经有解决方案了吗?
ATNConfig
个实例用于运行时的动态 DFA 构造。所需的实例数是语法和输入的函数。有一些可用的解决方案:
- 增加为应用程序提供的内存量。尝试
-Xmx12g
作为起点,看看问题是否是应用程序内存太少。
- 每个
ATNConfig
属于一个 DFA
实例,代表特定决策的 DFA。如果您知道包含最多 ATNConfig
个实例的决策,您可以在语法中简化这些决策。
- 通过调用
Recognizer.clearDFA()
定期清除缓存的 DFA。请注意,过于频繁地清除 DFA 会损害性能(如果可能,根本不要清除 DFA)。
- 您可以使用 ANTLR 4 的 "optimized" 分支。该项目的分支旨在减少内存占用,这可以极大地提高复杂语法的性能,但会牺牲某些简单语法的速度.
在使用 ANTLR 为 C++ 开发解析器时,我们制作了一个批处理解析测试用例,其中构造了一个新的解析器来解析每个 C++ 源文件。开始时的性能是可以接受的——每个文件大约 15 秒。但是在解析了大约 150 多个文件之后,每个文件的解析时间越来越长,最后 jvm 抛出一个 "GC limit exceeded" 错误。
使用JProfiler我们发现在解析每个文件后,有很多ATNConfig对象被逐步积累。从大约 70M 开始,它们逐渐堆积到超过 500M,直到堆接近满并且 GC 占用 100% CPU 时间。 JProfiler 识别的最大对象(在堆中保留最多对象的对象)包括 DFA[] 和 PredictionContextCache。
需要注意的一件事是,我们使用了 2 个线程来 运行 并发解析任务。尽管线程不共享任何解析器或解析树对象,但我们注意到 ANTLR 生成的解析器正在使用静态字段,这可能会导致多线程设置中的内存问题。反正就是个嫌疑人。
有谁知道 "ATNConfig being accumulated" 的原因是什么?已经有解决方案了吗?
ATNConfig
个实例用于运行时的动态 DFA 构造。所需的实例数是语法和输入的函数。有一些可用的解决方案:
- 增加为应用程序提供的内存量。尝试
-Xmx12g
作为起点,看看问题是否是应用程序内存太少。 - 每个
ATNConfig
属于一个DFA
实例,代表特定决策的 DFA。如果您知道包含最多ATNConfig
个实例的决策,您可以在语法中简化这些决策。 - 通过调用
Recognizer.clearDFA()
定期清除缓存的 DFA。请注意,过于频繁地清除 DFA 会损害性能(如果可能,根本不要清除 DFA)。 - 您可以使用 ANTLR 4 的 "optimized" 分支。该项目的分支旨在减少内存占用,这可以极大地提高复杂语法的性能,但会牺牲某些简单语法的速度.