繁体   English   中英

高负载下Groovy Shell解释器中的锁争用

[英]Lock contention in Groovy Shell interpreter under high load

我们正在评估应用程序中的GroovyShell解释器(v2.4),以动态执行标准Java语法。

之前,我们为此使用Java BeanShell Interpreter,但是在高负载下它有一个问题 ,促使我们寻找诸如Groovy的替代方案。

示例Java代码

static String script = "int y = x * x; System.out.println(\"** value of y ** :: \" + y ); ";

GroovyShell gs = new GroovyShell();
Script evalScript = gs.parse("void evalMethod() {" + script + "}");
// bind variables
Binding binding = new Binding();
binding.setVariable("x", 5);
evalScript.setBinding(binding);
// invoke eval method
evalScript.invokeMethod("evalMethod", null);

当多个线程同时执行上述代码时,我们看到线程锁争用。 我们有多个线程处于阻塞状态,这正在降低应用程序性能。

阻塞线程调用堆栈

java.lang.Thread.State: BLOCKED (on object monitor)
at java.lang.ClassLoader.loadClass(ClassLoader.java:404)
- waiting to lock <0x00000007bc425e40> (a java.lang.Object)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
at java.lang.ClassLoader.loadClass(ClassLoader.java:411)
- locked <0x00000007bd369ba8> (a groovy.lang.GroovyClassLoader)
at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:677)
at groovy.lang.GroovyClassLoader.loadClass(GroovyClassLoader.java:545)
at org.codehaus.groovy.control.ClassNodeResolver.tryAsLoaderClassOrScript(ClassNodeResolver.java:185)
at org.codehaus.groovy.control.ClassNodeResolver.findClassNode(ClassNodeResolver.java:170)
at org.codehaus.groovy.control.ClassNodeResolver.resolveName(ClassNodeResolver.java:126)
at org.codehaus.groovy.control.ResolveVisitor.resolveToOuter(ResolveVisitor.java:676)
at org.codehaus.groovy.control.ResolveVisitor.resolve(ResolveVisitor.java:313)
at org.codehaus.groovy.control.ResolveVisitor.transformPropertyExpression(ResolveVisitor.java:845)
at org.codehaus.groovy.control.ResolveVisitor.transform(ResolveVisitor.java:696)
at org.codehaus.groovy.control.ResolveVisitor.transformMethodCallExpression(ResolveVisitor.java:1081)
at org.codehaus.groovy.control.ResolveVisitor.transform(ResolveVisitor.java:702)
at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitExpressionStatement(ClassCodeExpressionTransformer.java:142)
at org.codehaus.groovy.ast.stmt.ExpressionStatement.visit(ExpressionStatement.java:42)
at org.codehaus.groovy.ast.CodeVisitorSupport.visitBlockStatement(CodeVisitorSupport.java:37)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitBlockStatement(ClassCodeVisitorSupport.java:166)
at org.codehaus.groovy.control.ResolveVisitor.visitBlockStatement(ResolveVisitor.java:1336)
at org.codehaus.groovy.ast.stmt.BlockStatement.visit(BlockStatement.java:71)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClassCodeContainer(ClassCodeVisitorSupport.java:104)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitConstructorOrMethod(ClassCodeVisitorSupport.java:115)
at org.codehaus.groovy.ast.ClassCodeExpressionTransformer.visitConstructorOrMethod(ClassCodeExpressionTransformer.java:53)
at org.codehaus.groovy.control.ResolveVisitor.visitConstructorOrMethod(ResolveVisitor.java:201)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitMethod(ClassCodeVisitorSupport.java:126)
at org.codehaus.groovy.ast.ClassNode.visitContents(ClassNode.java:1081)
at org.codehaus.groovy.ast.ClassCodeVisitorSupport.visitClass(ClassCodeVisitorSupport.java:53)
at org.codehaus.groovy.control.ResolveVisitor.visitClass(ResolveVisitor.java:1279)
at org.codehaus.groovy.control.ResolveVisitor.startResolving(ResolveVisitor.java:176)
at org.codehaus.groovy.control.CompilationUnit$12.call(CompilationUnit.java:663)
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:943)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:605)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:554)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:298)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:268)
- locked <0x00000007bd372240> (a java.util.HashMap)
at groovy.lang.GroovyShell.parseClass(GroovyShell.java:688)
at groovy.lang.GroovyShell.parse(GroovyShell.java:700)
at groovy.lang.GroovyShell.parse(GroovyShell.java:736)
at groovy.lang.GroovyShell.parse(GroovyShell.java:727)

我的问题是:

  1. 有没有人遇到过这样的问题? 如果是,请提出任何可能的解决方案? 我到处搜索这个问题,很少有博客建议缓存groovy Script对象。 Script对象是线程安全的吗? 此外,我需要将变量绑定到Script对象(通过groovy Binding对象,该对象对于不同线程中的每个执行而言是不同的),因此我认为缓存groovy Script对象不是一个可行的选择。
  2. 在Java中使用Groovy Shell解释器时,是否需要遵循一些最佳实践?
  3. 我们还可以考虑其他图书馆吗? 我的要求是动态执行标准Java语法。

常规解析和编译是相当繁重的部分

如果您的脚本是静态的-那么您只需解析一次

如果脚本总是不同但重复-您可以将已编译的Groovy脚本缓存到

ConcurrentHashMap<String, Class<groovy.lang.Script>>

Script evalScript = gs.parse(...);

只需将evalScript.getClass()放入已编译脚本的缓存中即可。

并在gs.parse(...)之前检查您是否已编译脚本,并仅获取该缓存类的新实例(如果存在)。

检查以下示例和执行速度

String script = "int y = x * x; System.out.println(\"** value of y ** :: \" + y ); ";

def ts = System.currentTimeMillis()
for(int i=0;i<100;i++){
    GroovyShell gs = new GroovyShell();
    Script evalScript = gs.parse("void evalMethod() {" + script + "}");
    // bind variables
    Binding binding = new Binding();
    binding.setVariable("x", i);
    evalScript.setBinding(binding);
    // invoke eval method
    evalScript.invokeMethod("evalMethod", null);
}
println ">> ${System.currentTimeMillis() - ts} millis"

1165毫

String script = "int y = x * x; System.out.println(\"** value of y ** :: \" + y ); ";
GroovyShell gs = new GroovyShell();
Class<Script> scriptClass = gs.parse("void evalMethod() {" + script + "}").getClass();

def ts = System.currentTimeMillis()
for(int i=0;i<100;i++){
    Script evalScript = scriptClass.newInstance();
    // bind variables
    Binding binding = new Binding();
    binding.setVariable("x", i);
    evalScript.setBinding(binding);
    // invoke eval method
    evalScript.invokeMethod("evalMethod", null);
}
println ">> ${System.currentTimeMillis() - ts} millis"

6毫秒

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM