首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用ASM检测后,同步数据块需要更多时间

使用ASM检测后,同步数据块需要更多时间
EN

Stack Overflow用户
提问于 2013-11-25 20:49:00
回答 1查看 360关注 0票数 2

我正在尝试使用ASM检测java同步块。问题是在检测之后,同步块的执行时间需要更多的时间。在Linux机器上,它从2毫秒增加到200毫秒。

我通过识别MonitorEnter和MonitorExit操作码来实现这一点。

我试着在三个层次上进行测试: 1.在MonitorEnter之前2.在MonitorEnter之后3.在MonitorExit之前。1和3一起工作很好,但当我使用2时,执行时间会急剧增加。

即使我们检测另一条SOP语句,这条语句只打算执行一次,它也会提供更高的值。下面是示例代码(质数,10个循环):

代码语言:javascript
运行
复制
for(int w=0;w<10;w++){
synchronized(s){
  long t1 = System.currentTimeMillis();
  long num = 2000;
for (long i = 1; i < num; i++) {
        long p = i;
    int j;
    for (j = 2; j < p; j++) {
            long n = p % i;
        }
    }
long t2 = System.currentTimeMillis();
 System.out.println("Time>>>>>>>>>>>> " + (t2-t1) );
}

这里的插装代码(这里的System.currentMilliSeconds()给出了插装发生的时间,它不是执行时间的度量,执行时间来自obove SOP语句):

代码语言:javascript
运行
复制
  public void visitInsn(int opcode)
    {
        switch(opcode)
        {
          // Scenario 1
        case 194: 
            visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io   /PrintStream;");
            visitLdcInsn("TIME Arrive: "+System.currentTimeMillis());
            visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
            break;

        // scenario 3
        case 195: 
            visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            visitLdcInsn("TIME exit : "+System.currentTimeMillis());
            visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
            break;
        }

        super.visitInsn(opcode);

       // scenario 2
       if(opcode==194)
        {
            visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
            visitLdcInsn("TIME enter: "+System.currentTimeMillis());
            visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");

        }

        }

我不能找到它发生的原因和如何纠正它。

提前谢谢。

EN

回答 1

Stack Overflow用户

发布于 2013-11-26 21:23:17

原因在于用于运行代码的JVM的内部结构。我假设这是一个HotSpot JVM,但是下面的答案对于大多数其他实现都是同样正确的。

如果您触发以下代码:

代码语言:javascript
运行
复制
int result = 0;
for(int i = 0; i < 1000; i++) {
  result += i;
}

这将由Java编译器直接转换成Java字节码,但是在运行时,JVM会很容易地看到这段代码什么也没做。执行此代码不会对外部(应用程序)世界产生任何影响,那么为什么JVM要执行它呢?这正是编译器优化为您做的事情。

但是,如果您触发以下代码:

代码语言:javascript
运行
复制
int result = 0;
for(int i = 0; i < 1000; i++) {
  System.out.println(result);
}

Java运行时不能再优化您的代码。整个循环必须始终运行,因为System.out.println(int)方法总是在做一些实际的事情,这样代码的运行速度就会变慢。

现在让我们看一下你的例子。在您的第一个示例中,您基本上编写了以下代码:

代码语言:javascript
运行
复制
synchronized(s) {
   // do nothing useful
}

Java运行时可以很容易地删除整个代码块。这意味着:将不会有同步!在第二个示例中,您将编写以下代码:

代码语言:javascript
运行
复制
synchronized(s) {
   long t1 = System.currentTimeMillis();
   // do nothing useful
   long t2 = System.currentTimeMillis();
   System.out.println("Time>>>>>>>>>>>> " + (t2-t1));
}

这意味着有效的代码可能如下所示:

代码语言:javascript
运行
复制
synchronized(s) {
   long t1 = System.currentTimeMillis();
   long t2 = System.currentTimeMillis();
   System.out.println("Time>>>>>>>>>>>> " + (t2-t1));
}

这里重要的是,优化后的代码将被有效地同步,这是在执行时间方面的一个重要区别。基本上,您正在测量同步某些内容所花费的时间(如果JVM意识到s没有锁定在您的代码中的其他位置,那么在几次运行后可能会优化掉这个时间)(流行语:临时优化,如果将来加载的代码也将在s上同步,则可能会取消优化)。

你真应该读一读这篇文章:

例如,您的测试错过了一次预热,因此您还在测量JVM将用于字节码到机器码优化的时间。

顺便说一句:在String上同步几乎总是一个坏主意。您的字符串可能是也可能不是interned,这意味着您不能绝对确定它们的身份。这意味着,同步可能起作用,也可能不起作用,您甚至可能对代码的其他部分进行同步。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20193548

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档