Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/369.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 奇怪的优化器行为:JVM坚持一种优化,不';不适应_Java_Jvm_Openjdk_Jmh - Fatal编程技术网

Java 奇怪的优化器行为:JVM坚持一种优化,不';不适应

Java 奇怪的优化器行为:JVM坚持一种优化,不';不适应,java,jvm,openjdk,jmh,Java,Jvm,Openjdk,Jmh,我有一个漫长的过程,主要有两个阶段。第一阶段和第二阶段的执行路径略有不同 我刚刚意识到,根据JMH(-server,onjava-7-openjdk-amd64)的说法,如果我用不同的名称创建同一方法的副本,并在每个阶段使用不同的名称,那么在第二阶段中,我的方法调用的加速率将超过25%(在5次调用预热后,对该方法的调用超过数百万次,以5次调用衡量) 有没有办法告诉JVM忘记以前对方法的优化,从头开始重新学习? 在下面的示例代码中,基准方法是run,在stage2中调用checkChar和chec

我有一个漫长的过程,主要有两个阶段。第一阶段和第二阶段的执行路径略有不同

我刚刚意识到,根据JMH(
-server
,on
java-7-openjdk-amd64
)的说法,如果我用不同的名称创建同一方法的副本,并在每个阶段使用不同的名称,那么在第二阶段中,我的方法调用的加速率将超过25%(在5次调用预热后,对该方法的调用超过数百万次,以5次调用衡量)

有没有办法告诉JVM忘记以前对方法的优化,从头开始重新学习?

在下面的示例代码中,基准方法是
run
,在
stage2
中调用
checkChar
checkChar0
的两个版本之间进行比较

final public void run(){
   sumStg1=0;
   for(int i=0; i< 10000; i++){
      String str = consumeString();
      for(int i= 0; i<K; i++){
         sumStg1 += checkChar(str.charAt(i), i)?1:0;
      }
   }

   sumStg2=0;
   for(int j=0; j< 10000000; j++){
      String str = consumeString();
      for(int i=K/2; i<str.length(); i++){
         sumStg2 += checkChar(str.charAt(i), i)?1:0;
      }
   }
}

final public boolean checkChar(char in, int i){
   if(i < K/2){
     ...
   } else if (i < K){
     ...
   } else {
     ...
   }
}

//identical method to checkChar
final public boolean checkChar0(char in, int i){
   if(i < K/2){
     ...
   } else if (i < K){
     ...
   } else {
     ...
   }
}
final public void run(){
sumStg1=0;
对于(int i=0;i<10000;i++){
String str=consumerString();

对于(int i=0;i我想到了两个想法:

A) 通过调用并使用指向同一方法的新方法句柄更新该方法,然后调用应触发调用方法的重新编译

A提供了类似的功能

我不知道JVM是否会在这里应用任何技巧,如果方法被替换为它自己,就跳过重新编译

B) 应用一个本质上不会改变任何东西的类文件,除了可能向方法体附加一些惰性字节码或其他什么

请注意,我还没有尝试过这些,所以不能保证


还有一种可能是,您没有对您打算进行基准测试的内容进行基准测试。JMH并不是通过在代码中挥动它来消除所有测量问题的魔棒

您说您的实际应用程序由两个阶段组成,并且有一个阶段转换

但在JMH中,您总共使用了10次迭代(每次调用N次),这意味着在新迭代开始时,将有10*N次前向阶段转换和10*N次后向转换。这将最终导致JIT放弃重新编译


JMH旨在测量稳态性能,而您的应用程序依赖于一次性行为。

另外两个想法:

  • 在新的类加载器中再次加载该类,并使用该版本而不是静态加载的版本

  • -XX:CompileThreshold
    设置更大的值


(这两种方法都有显著/明显的缺点,但如果性能对您如此重要……)

这两个选项看起来都很有趣。然而,这种粘性优化器行为是否违背了面向对象的核心原则?子类以不同的方式使用继承的方法是非常合理的,并且优化将针对在程序/服务的开始阶段最常使用它的子类。Am我在这里遗漏了什么?不是全局使用了哪些(子)类(尽管类层次结构分析也有影响),而是调用站点使用了哪些类(假设代码已内联到该调用站点)。因此,如果您在特定的调用站点更改类型配置文件,这对优化器来说是一个问题。它会尝试适应几次,但最终放弃不浪费CPU周期的反复优化。也许您问错了问题…单独的类加载器听起来确实像是一种实用的方法。它让我思考我是否我们会使用匿名类来实现同样的效果。不……那没有帮助。匿名类会被加载……并进行优化……就像一个普通的命名类一样。“匿名”仅仅意味着你不能在Java源代码中命名该类。