Skip to content

Instantly share code, notes, and snippets.

@caoxudong
Last active March 25, 2021 02:44
Show Gist options
  • Save caoxudong/08ab363725dcdec6aabbbc1e14cdbcc7 to your computer and use it in GitHub Desktop.
Save caoxudong/08ab363725dcdec6aabbbc1e14cdbcc7 to your computer and use it in GitHub Desktop.
关于MethodHandle性能的简单测试
package test;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
public class MethodHandleTest {
public static void main(String[] args) throws Throwable {
int warmupThreshold = 10000000;
Method method = null;
MethodHandle methodHandle = null;
MethodHandleTest receiver = new MethodHandleTest();
long beginTime = 0L;
long endTime = 0L;
System.out.println("warm-up begin");
System.out.println("timing warm-up begin");
for (int i = 0; i < warmupThreshold; i++) {
beginTime = System.currentTimeMillis();
}
System.out.println("timing warm-up end");
System.out.println("plain call warm-up begin");
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
receiver.add(i, i + 1);
}
endTime = System.currentTimeMillis();
System.out.println("plain call warm-up end, time consumed = " + (endTime - beginTime));
System.out.println("reflection call warm-up begin");
method = MethodHandleTest.class.getMethod("add", new Class[] {Integer.TYPE, Integer.TYPE});
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
method.invoke(receiver, new Object[] {i, i + 1});
}
endTime = System.currentTimeMillis();
System.out.println("reflection call warm-up end, time consumed = " + (endTime - beginTime));
System.out.println("methodhandle call warm-up begin");
methodHandle = MethodHandles.lookup().findVirtual(MethodHandleTest.class, "add",
MethodType.methodType(Integer.TYPE, new Class[] {Integer.TYPE, Integer.TYPE})).bindTo(receiver);
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
methodHandle.invoke(i, i+1);
}
endTime = System.currentTimeMillis();
System.out.println("methodhandle call warm-up end, time consumed = " + (endTime - beginTime));
System.out.println("warm-up end");
System.out.println("test begin");
System.out.println("plain call test begin");
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
receiver.add(i, i + 1);
}
endTime = System.currentTimeMillis();
System.out.println("plain call test end, time consumed = " + (endTime - beginTime));
System.out.println("reflection call test begin");
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
method.invoke(receiver, new Object[] {i, i + 1});
}
endTime = System.currentTimeMillis();
System.out.println("reflection call test end, time consumed = " + (endTime - beginTime));
System.out.println("methodhandle call warm-up begin");
beginTime = System.currentTimeMillis();
for (int i = 0; i < warmupThreshold; i++) {
methodHandle.invoke(i, i+1);
}
endTime = System.currentTimeMillis();
System.out.println("methodhandle call warm-up end, time consumed = " + (endTime - beginTime));
}
public int add(int a, int b) {
return a + b;
}
}

代码执行结果如下:

[xiaoju@ed3e13fd9af6 test_method_handle]$ java -cp . MethodHandleTest
warm-up begin
timing warm-up begin
timing warm-up end
plain call warm-up begin
plain call warm-up end, time consumed = 7
reflection call warm-up begin
reflection call warm-up end, time consumed = 191
methodhandle call warm-up begin
methodhandle call warm-up end, time consumed = 73
warm-up end
test begin
plain call test begin
plain call test end, time consumed = 10
reflection call test begin
reflection call test end, time consumed = 155
methodhandle call warm-up begin
methodhandle call warm-up end, time consumed = 66
[xiaoju@ed3e13fd9af6 test_method_handle]$

    从中可以看到,使用MethodHandle的执行效率比反射高不少。添加打印编译信息,结果如下:

[xiaoju@ed3e13fd9af6 test_method_handle]$ java -XX:+PrintCompilation -cp . MethodHandleTest
     62    1       3       java.lang.String::charAt (29 bytes)
     62    2       3       java.lang.String::indexOf (70 bytes)
     62    3       3       java.lang.String::hashCode (55 bytes)
     62    4       3       java.lang.String::length (6 bytes)
     64    5       3       java.lang.Object::<init> (1 bytes)
     65    6       3       java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
     65    7     n 0       java.lang.System::arraycopy (native)   (static)
     65    8       3       java.lang.String::equals (81 bytes)
     65    9       3       java.lang.Math::min (11 bytes)
     65   11       1       java.lang.ref.Reference::get (5 bytes)
     65   10       3       java.util.Arrays::copyOfRange (63 bytes)
     65   12       3       java.lang.String::<init> (82 bytes)
     66   13       1       java.lang.ThreadLocal::access$400 (5 bytes)
     67   14       3       java.lang.String::getChars (62 bytes)
     69   15       3       java.lang.System::getSecurityManager (4 bytes)
warm-up begin
timing warm-up begin
     74   16     n 0       java.lang.System::currentTimeMillis (native)   (static)
     78   17 %     3       MethodHandleTest::main @ 41 (619 bytes)
     84   18       3       MethodHandleTest::main (619 bytes)
     89   19 %     4       MethodHandleTest::main @ 41 (619 bytes)
     90   17 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    479   19 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
timing warm-up end
plain call warm-up begin
    479   21 %     3       MethodHandleTest::main @ 82 (619 bytes)
    479   20       3       MethodHandleTest::add (4 bytes)
    480   22       1       MethodHandleTest::add (4 bytes)
    480   20       3       MethodHandleTest::add (4 bytes)   made not entrant
    485   23 %     4       MethodHandleTest::main @ 82 (619 bytes)
    486   21 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    486   23 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
plain call warm-up end, time consumed = 7
reflection call warm-up begin
    488   24       3       sun.reflect.ClassFileAssembler::emitByte (11 bytes)
    488   25       3       sun.reflect.ByteVectorImpl::add (38 bytes)
    489   27       3       sun.reflect.ByteVectorImpl::getLength (7 bytes)
    489   28       3       sun.reflect.ByteVectorImpl::get (26 bytes)
    489   26       3       sun.reflect.ClassFileAssembler::emitShort (24 bytes)
    490   29       1       java.lang.Integer::intValue (5 bytes)
    490   31       3       java.lang.Number::<init> (5 bytes)
    490   30       3       java.lang.Integer::valueOf (32 bytes)
    490   32       3       java.lang.Integer::<init> (10 bytes)
    490   34     n 0       sun.reflect.Reflection::getClassAccessFlags (native)   (static)
    490   33       3       java.lang.reflect.Modifier::isPublic (12 bytes)
    490   35       3       sun.reflect.Reflection::quickCheckMemberAccess (10 bytes)
    491   36       3       java.lang.reflect.Method::invoke (62 bytes)
    491   37       3       sun.reflect.DelegatingMethodAccessorImpl::invoke (10 bytes)
    491   38   !   3       sun.reflect.GeneratedMethodAccessor1::invoke (228 bytes)
    491   39 %     3       MethodHandleTest::main @ 180 (619 bytes)
    492   40       4       java.lang.Integer::valueOf (32 bytes)
    493   30       3       java.lang.Integer::valueOf (32 bytes)   made not entrant
    493   41       4       java.lang.reflect.Method::invoke (62 bytes)
    494   42       4       sun.reflect.DelegatingMethodAccessorImpl::invoke (10 bytes)
    494   43   !   4       sun.reflect.GeneratedMethodAccessor1::invoke (228 bytes)
    498   38   !   3       sun.reflect.GeneratedMethodAccessor1::invoke (228 bytes)   made not entrant
    498   37       3       sun.reflect.DelegatingMethodAccessorImpl::invoke (10 bytes)   made not entrant
    499   36       3       java.lang.reflect.Method::invoke (62 bytes)   made not entrant
    502   44 %     4       MethodHandleTest::main @ 180 (619 bytes)
    510   39 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    678   44 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
reflection call warm-up end, time consumed = 191
methodhandle call warm-up begin
    680   45       3       java.lang.Byte::<init> (10 bytes)
    680   46       3       java.lang.Short::<init> (10 bytes)
    681   40       4       java.lang.Integer::valueOf (32 bytes)   made not entrant
    681   47       3       java.lang.Long::<init> (10 bytes)
    683   48     n 0       java.lang.invoke.MethodHandle::linkToVirtual(LIIL)I (native)   (static)
    684   49       3       java.util.concurrent.ConcurrentHashMap::tabAt (21 bytes)
    685   50     n 0       sun.misc.Unsafe::getObjectVolatile (native)
    685   51       1       java.lang.Object::<init> (1 bytes)
    685    5       3       java.lang.Object::<init> (1 bytes)   made not entrant
    687   52     n 0       java.lang.invoke.MethodHandle::linkToStatic(LLLL)L (native)   (static)
    687   53       1       java.lang.reflect.Method::getName (5 bytes)
    687   54     n 0       java.lang.invoke.MethodHandle::linkToStatic(LL)L (native)   (static)
    692   55     n 0       java.lang.Object::hashCode (native)
    692   56     n 0       java.lang.invoke.MethodHandle::linkToStatic(LL)V (native)   (static)
    692   57       4       java.lang.String::charAt (29 bytes)
    692   58       3       java.lang.String::indexOf (7 bytes)
    692   59       1       java.lang.Class::getClassLoader0 (5 bytes)
    692   60     n 0       java.lang.invoke.MethodHandle::linkToStatic(LL)J (native)   (static)
    693    1       3       java.lang.String::charAt (29 bytes)   made not entrant
    693   61       3       java.lang.String::lastIndexOf (52 bytes)
    693   62     n 0       java.lang.invoke.MethodHandle::linkToStatic(LLL)L (native)   (static)
    694   63       1       java.lang.Enum::ordinal (5 bytes)
    694   64     n 0       java.lang.Class::isPrimitive (native)
    694   65       3       java.lang.invoke.MemberName::testFlags (16 bytes)
    694   66       3       java.lang.Class::getName (21 bytes)
    695   67       3       java.lang.ref.Reference::<init> (25 bytes)
    695   68       1       java.lang.invoke.MethodType::returnType (5 bytes)
    695   69       3       java.lang.AbstractStringBuilder::append (29 bytes)
    695   70       1       java.lang.invoke.MemberName::getDeclaringClass (5 bytes)
    696   71       3       jdk.internal.org.objectweb.asm.ByteVector::putUTF8 (142 bytes)
    696   72       3       java.lang.AbstractStringBuilder::append (50 bytes)
    696   74       3       jdk.internal.org.objectweb.asm.ClassWriter::get (49 bytes)
    696   73       3       jdk.internal.org.objectweb.asm.Item::set (143 bytes)
    696   75       3       java.lang.StringBuilder::append (8 bytes)
    696   77       3       java.lang.StringBuilder::append (8 bytes)
    696   76       3       jdk.internal.org.objectweb.asm.ByteVector::putShort (52 bytes)
    697   78       3       jdk.internal.org.objectweb.asm.Item::<init> (66 bytes)
    697   79       3       jdk.internal.org.objectweb.asm.ClassWriter::put (152 bytes)
    697   80       3       java.lang.AbstractStringBuilder::<init> (12 bytes)
    697   81       1       java.lang.invoke.MethodType$ConcurrentWeakInternSet$WeakEntry::hashCode (5 bytes)
    697   82       3       java.lang.AbstractStringBuilder::newCapacity (39 bytes)
    698   83       3       jdk.internal.org.objectweb.asm.ByteVector::putByte (39 bytes)
    698   84       3       jdk.internal.org.objectweb.asm.ClassWriter::newUTF8 (70 bytes)
    699   88       3       java.lang.invoke.MemberName::testAnyFlags (15 bytes)
    699   87       1       java.util.SubList::access$200 (5 bytes)
    699   86       1       java.util.AbstractList$ListItr::nextIndex (5 bytes)
    699   85       1       java.util.SubList::access$000 (5 bytes)
    699   89       3       java.lang.Class::getClassLoader (28 bytes)
    699   90       3       java.util.concurrent.ConcurrentHashMap::spread (10 bytes)
    699   91       1       java.lang.String::length (6 bytes)
    700    4       3       java.lang.String::length (6 bytes)   made not entrant
    700   92       1       java.util.ArrayList::access$100 (5 bytes)
    700   93       3       java.util.Objects::requireNonNull (14 bytes)
    700   94       3       java.lang.invoke.MemberName::isInvocable (7 bytes)
    700   95       3       java.util.concurrent.ConcurrentHashMap::setTabAt (19 bytes)
    700   96     n 0       sun.misc.Unsafe::putObjectVolatile (native)
    701   97       3       java.lang.String::replace (127 bytes)
    701   98       1       java.util.ArrayList::size (5 bytes)
    701   99       3       java.lang.String::lastIndexOf (13 bytes)
    702  100       3       java.lang.ref.Reference::<init> (7 bytes)
    702  101   !   3       java.util.concurrent.ConcurrentHashMap::putVal (362 bytes)
    702  102     n 0       java.lang.Object::getClass (native)
    702  103   !   3       java.lang.ref.ReferenceQueue::poll (28 bytes)
    703  104       3       java.util.concurrent.ConcurrentHashMap::putIfAbsent (8 bytes)
    703  105     n 0       java.lang.invoke.MethodHandle::linkToStatic(LLL)V (native)   (static)
    703  106       3       java.lang.StringBuilder::toString (17 bytes)
    704  107     n 0       java.lang.invoke.MethodHandle::invokeBasic(LII)I (native)
    704  108     n 0       java.lang.invoke.MethodHandle::linkToSpecial(LLIIL)I (native)   (static)
    704  109       3       java.lang.invoke.MethodTypeForm::canonicalize (233 bytes)
    704  110       1       sun.invoke.util.Wrapper::basicTypeChar (5 bytes)
    705  112       3       java.lang.invoke.MethodType::hashCode (53 bytes)
    705  111       3       java.lang.invoke.MethodType$ConcurrentWeakInternSet::expungeStaleElements (27 bytes)
    706  113       3       java.lang.StringBuilder::<init> (7 bytes)
    707  114       1       java.lang.invoke.MethodType::ptypes (5 bytes)
    707  115     n 0       java.lang.Class::isArray (native)
    707  117       3       java.lang.invoke.MethodType::checkSlotCount (33 bytes)
    707  116       3       java.lang.reflect.Modifier::isStatic (13 bytes)
    708  118     n 0       java.lang.invoke.MethodHandle::linkToStatic(LLLLL)L (native)   (static)
    708  119       3       java.lang.invoke.MemberName::isStatic (8 bytes)
    708  120       3       jdk.internal.org.objectweb.asm.ByteVector::put12 (64 bytes)
    709  121     n 0       java.lang.invoke.MethodHandle::invokeBasic(LLLL)L (native)
    709  122       3       jdk.internal.org.objectweb.asm.Frame::execute (2252 bytes)
    709  123       3       java.lang.String::substring (79 bytes)
    710  124       3       java.lang.invoke.MethodType::checkPtype (19 bytes)
    710  125     n 0       java.lang.invoke.MethodHandle::invokeBasic(II)V (native)
    710  126     n 0       java.lang.invoke.MethodHandle::linkToSpecial(LIIL)V (native)   (static)
    710  127       1       java.lang.invoke.MethodType::form (5 bytes)
    710  128     n 0       java.lang.Object::clone (native)
    710  129       3       java.lang.invoke.MemberName::testAllFlags (7 bytes)
    711  130       3       java.lang.String::indexOf (166 bytes)
    711  131       1       java.lang.invoke.LambdaForm$Name::index (5 bytes)
    711  132       4       java.lang.String::hashCode (55 bytes)
    712  133       3       jdk.internal.org.objectweb.asm.Item::isEqualTo (354 bytes)
    712  134       3       java.lang.invoke.DirectMethodHandle::internalMemberName (8 bytes)
    712  136       3       java.lang.invoke.Invokers::checkGenericType (16 bytes)
    712  135       3       java.lang.invoke.LambdaForm$MH/1950409828::invoke_MT (26 bytes)
    712  137       3       java.lang.invoke.MethodHandle::asType (28 bytes)
    713  138       3       java.lang.invoke.MethodHandle::asTypeCached (21 bytes)
    713  140       3       java.lang.invoke.LambdaForm$DMH/1670675563::invokeVirtual_LII_I (18 bytes)
    713  139       3       java.lang.invoke.Invokers::checkCustomized (20 bytes)
    713  142 %     3       MethodHandleTest::main @ 309 (619 bytes)
    713  141       3       java.lang.invoke.LambdaForm$BMH/2016447921::reinvoke (34 bytes)
    714  143       4       java.lang.invoke.LambdaForm$MH/1950409828::invoke_MT (26 bytes)
    714  144       4       java.lang.invoke.LambdaForm$BMH/2016447921::reinvoke (34 bytes)
    714    3       3       java.lang.String::hashCode (55 bytes)   made not entrant
    714  141       3       java.lang.invoke.LambdaForm$BMH/2016447921::reinvoke (34 bytes)   made not entrant
    716  135       3       java.lang.invoke.LambdaForm$MH/1950409828::invoke_MT (26 bytes)   made not entrant
    721  145       4       java.lang.invoke.MethodHandle::asType (28 bytes)
    721  146 %     4       MethodHandleTest::main @ 309 (619 bytes)
    721  137       3       java.lang.invoke.MethodHandle::asType (28 bytes)   made not entrant
    724  142 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    782  146 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
methodhandle call warm-up end, time consumed = 73
warm-up end
test begin
plain call test begin
    782  147 %     3       MethodHandleTest::main @ 397 (619 bytes)
    792  148 %     4       MethodHandleTest::main @ 397 (619 bytes)
    794  147 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    794  148 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
plain call test end, time consumed = 12
reflection call test begin    795
 149       1       java.nio.Buffer::position (5 bytes)
    795  150       3       java.lang.Integer::valueOf (32 bytes)
    795  151       4       java.lang.Integer::<init> (10 bytes)
    795  152 %     3       MethodHandleTest::main @ 471 (619 bytes)
    795   32       3       java.lang.Integer::<init> (10 bytes)   made not entrant
    796  153       4       java.lang.Integer::valueOf (32 bytes)
    797  150       3       java.lang.Integer::valueOf (32 bytes)   made not entrant
    805  154 %     4       MethodHandleTest::main @ 471 (619 bytes)
    816  152 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
    948  154 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
reflection call test end, time consumed = 153
methodhandle call warm-up begin
    949  155 %     3       MethodHandleTest::main @ 562 (619 bytes)
    957  156 %     4       MethodHandleTest::main @ 562 (619 bytes)
    960  155 %     3       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
   1014  156 %     4       MethodHandleTest::main @ -2 (619 bytes)   made not entrant
methodhandle call warm-up end, time consumed = 65
[xiaoju@ed3e13fd9af6 test_method_handle]$
[xiaoju@ed3e13fd9af6 test_method_handle]$ uname -a
Linux ed3e13fd9af6 3.10.0-327.36.3.el7.x86_64 #1 SMP Mon Oct 24 16:09:20 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
[xiaoju@ed3e13fd9af6 test_method_handle]$



[xiaoju@ed3e13fd9af6 test_method_handle]$ free -m
              total        used        free      shared  buff/cache   available
Mem:         257709       51559       83923        5410      122226      199262
Swap:        138700        5419      133281
[xiaoju@ed3e13fd9af6 test_method_handle]$


[xiaoju@ed3e13fd9af6 test_method_handle]$ java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
[xiaoju@ed3e13fd9af6 test_method_handle]$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment