とりあえず、JVMCI経由でメソッドプロファイル情報を取得してみます。
- プロファイルを取得したいクラスを予め決めておき、シャットダウンフック実行時に一気にダンプします。
- JVMCIではメソッド単位でコントロール可能ですが、とりあえずクラス内定義メソッド全部をダンプするようにします。
/*
* Copyright (C) 2015 Yasumasa Suenaga
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
import java.util.*;
import jdk.vm.ci.runtime.*;
import jdk.vm.ci.hotspot.*;
/* Please run with -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI */
public class JVMCIWatch{
private final List<Class<?>> classList;
public JVMCIWatch(List<Class<?>> classList){
this.classList = classList;
Runtime.getRuntime().addShutdownHook(new Thread(() -> dumpProfiles()));
System.out.println("Watch targets:");
classList.stream()
.forEach(c -> System.out.println(" " + c.toString()));
}
public void dumpProfiles(){
JVMCIBackend backend = JVMCI.getRuntime().getHostJVMCIBackend();
HotSpotMetaAccessProvider metaAccess =
(HotSpotMetaAccessProvider)backend.getMetaAccess();
System.out.println("Profile data:");
classList.stream()
.flatMap(c -> Arrays.stream(c.getMethods()))
.peek(m -> System.out.print(m.toString() + ": "))
.map(metaAccess::lookupJavaMethod)
.map(m -> m.getProfilingInfo(true, true))
.forEach(System.out::println);
}
}
/*
* Copyright (C) 2015 Yasumasa Suenaga
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
import java.util.*;
public class JVMCITest{
public static void runLoop(int max){
int result = 0;
for(int i = 1; i <= max; i++){
result += i;
}
System.out.println(result);
}
public static void main(String[] args) throws Exception{
List<Class<?>> classList = new ArrayList<>();
classList.add(JVMCITest.class);
classList.add(JVMCIWatch.class);
new JVMCIWatch(classList);
runLoop(1000000);
}
}
実行時に必ず -XX:+UnlockExperimentalVMOptions
と -XX:+EnableJVMCI
の2つを付与してください。
$ /usr/local/jdk-9/bin/javac *.java
$ /usr/local/jdk-9/bin/java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI JVMCITest
Watch targets:
class JVMCITest
class JVMCIWatch
1784293664
Profile data:
public static void JVMCITest.runLoop(int): HotSpotProfilingInfo<exceptionSeen@0: FALSE; exceptionSeen@1: FALSE; exceptionSeen@2: FALSE; exceptionSeen@3: FALSE; exceptionSeen@4: FALSE; exceptionSeen@5: FALSE; executionCount@6: 600064; branchProbability@6: 0.000002; exceptionSeen@6: FALSE; exceptionSeen@7: FALSE; exceptionSeen@8: FALSE; exceptionSeen@9: FALSE; exceptionSeen@10: FALSE; exceptionSeen@11: FALSE; exceptionSeen@12: FALSE; exceptionSeen@13: FALSE; exceptionSeen@14: FALSE; exceptionSeen@15: FALSE; executionCount@16: 600063; branchProbability@16: 1.000000; exceptionSeen@16: FALSE; exceptionSeen@17: FALSE; exceptionSeen@18: FALSE; exceptionSeen@19: FALSE; exceptionSeen@20: FALSE; exceptionSeen@21: FALSE; exceptionSeen@22: FALSE; executionCount@23: 1; exceptionSeen@23: FALSE; nullSeen@23: FALSE; types@23: 1.000000 (HotSpotType<Ljava/io/PrintStream;, resolved>); <no other types>; exceptionSeen@24: FALSE; exceptionSeen@25: FALSE; exceptionSeen@26: FALSE>
public static void JVMCITest.main(java.lang.String[]) throws java.lang.Exception: DefaultProfilingInfo<>
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException: DefaultProfilingInfo<>
public final void java.lang.Object.wait() throws java.lang.InterruptedException: DefaultProfilingInfo<>
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException: DefaultProfilingInfo<>
public boolean java.lang.Object.equals(java.lang.Object): DefaultProfilingInfo<>
public java.lang.String java.lang.Object.toString(): DefaultProfilingInfo<>
public native int java.lang.Object.hashCode(): DefaultProfilingInfo<>
public final native java.lang.Class java.lang.Object.getClass(): DefaultProfilingInfo<>
public final native void java.lang.Object.notify(): DefaultProfilingInfo<>
public final native void java.lang.Object.notifyAll(): DefaultProfilingInfo<>
public void JVMCIWatch.dumpProfiles(): DefaultProfilingInfo<>
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException: DefaultProfilingInfo<>
public final void java.lang.Object.wait() throws java.lang.InterruptedException: DefaultProfilingInfo<>
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException: DefaultProfilingInfo<>
public boolean java.lang.Object.equals(java.lang.Object): DefaultProfilingInfo<>
public java.lang.String java.lang.Object.toString(): DefaultProfilingInfo<>
public native int java.lang.Object.hashCode(): DefaultProfilingInfo<>
public final native java.lang.Class java.lang.Object.getClass(): DefaultProfilingInfo<>
public final native void java.lang.Object.notify(): DefaultProfilingInfo<>
public final native void java.lang.Object.notifyAll(): DefaultProfilingInfo<>
インタプリタモードでは DefaultProfilingInfo
、JITコンパイル済みコードは HotSpotProfilingInfo
のインスタンスとして表示されるようです(あまり深追いしていません)。
深追いした結果を Java Day Tokyo 2017のD1-C1で発表しました。資料とサンプルコードも公開しているので、ぜひご覧ください。