我正在尝试使用在Java应用程序中调用的groovy eval(string)方法来追踪性能问题的根源。如果我执行以下代码;
String pattern = "test = ['one','two','three']";
engine.eval(pattern)
它几乎没有时间(0到1毫秒)运行
但是,如果我说以下
String first = "['one','two','three']";
String pattern = "test = " + first; // "identical" String to first approach
engine.eval(pattern)
运行需要~30 + ms。
更糟糕的是,经过数千次调用后,它将高达60-70ms,尽管我对这一点的关注程度远远低于两次实现之间的时间差异。
关于为什么会发生这种情况的任何解释/关于如何避免这种情况的建议?我怀疑它与Java和/或Groovy编译器有关,我已经开始查看compile()方法,但是我更喜欢它,如果有一种简单的方法可以使现有的代码工作(更少的东西要改变它方式)。
答案 0 :(得分:2)
如果查看https://github.com/groovy/groovy-core/blob/master/subprojects/groovy-jsr223/src/main/java/org/codehaus/groovy/jsr223/GroovyScriptEngineImpl.java?source=cc,您会找到一个用于将字符串映射到类的缓存。问题是ManagedConcurrentMap基本上是一个标识哈希映射,这意味着如果你反复使用相同的字符串,它会很快,因为后续运行会跳过编译。问题版本每次都会创建一个新字符串,因此每次都需要编译它,每次都会生成一个新类。
关于如何避免这种情况:创建基于哈希的缓存
答案 1 :(得分:0)
eval
任何一种语言都是出了名的慢,经常被建议远离(" eval是邪恶的")。但是,在你的代码中,字符串连接似乎更多地导致减速,而不是eval本身。
仍然,几毫秒不应该引起关注 - 您是否已确定这会导致代码中出现性能瓶颈?微管理代码可能会导致未来的错误和不清楚的代码,这可能会产生更糟糕的影响。
答案 2 :(得分:0)
您如何衡量调用的时间?如果可能的话,我会测量每个部分(String构建和eval())以确定哪个花费时间,哪个随着迭代次数而增加。因为你说时间随着迭代次数的增加而增加,所以看看垃圾收集。第一种情况使用单个String - 在后一种情况下,每次迭代都要构建一个新的String,因此消耗内存。您可能正在遇到堆限制。
用于查看此内容的一个非常有用的工具是VisualVM。 http://visualvm.java.net/
答案 3 :(得分:0)
最终我们认识到了
eval("['one','two','three']")
完全返回,所以
Object test = engine.eval(first)
相当于
engine.eval("test = " + "['one','two','three']") // see text above for exact syntax
Object test = engine.get("test")
然而它根本没有时间运行。
我仍然很好奇为什么使模式动态对eval()的性能产生如此巨大的影响但是我怀疑需要对Java和/或groovy运行时元素有深刻理解的人才能放弃点亮它。
感谢所有试图提供帮助的人。 的问候,
PS。