Quantcast
Viewing latest article 9
Browse Latest Browse All 36

What?! 3 vs 36 MiB for a no-op 254-ary method call via a method handle

It could be shown (see below a rewritten test case) that method handles for 254-ary methods claim less memory in Java 17 than in Java 11, when compiled and run by its tools.

Since no method-handle- or reflection-related features are advertised in release summaries for Java versions from 11 to 17, I'm curious: what changes have contributed to less memory consumption?


This is a rewritten test case ArityLimits.java:

import java.lang.invoke.MethodHandle;import java.lang.invoke.MethodHandles.Lookup;import java.lang.invoke.MethodHandles;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.util.Collections;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.function.Consumer;import java.util.function.Supplier;class ArityLimits{    public static void main(String[] args) throws Throwable    {        // Pick an implementation with the 1st argument, e.g. core.        final Invocable invocable = (args.length > 0&& args[0].equalsIgnoreCase("core"))            ? new CoreInvoker(ArityLimits::new)            : new HandleInvoker(ArityLimits::new,                MethodHandles.privateLookupIn(                        ArityLimits.class,                        MethodHandles.lookup()));        // Pick which methods to call with the 2nd argument, e.g.        // $(( 1|2|4|8 )).        final int agenda = (args.length > 1)            ? 0xf & Integer.parseInt(args[1])            : (1 | 2 | 4 | 8);        final Map<String, List<?>> arguments = new HashMap<>(8);        if ((agenda & 1) != 0)            arguments.put("passCrunchLongsFix128",                Collections.nCopies(128 - 2 /* (handle, this) */, 0L));        if ((agenda & 2) != 0)            arguments.put("passClassCrunchLongsFix128",                Collections.nCopies(128 - 1 /* (handle) */, 0L));        if ((agenda & 4) != 0)            arguments.put("passCrunchIntsFix255",                Collections.nCopies(255 - 2 /* (handle, this) */, 0));        if ((agenda & 8) != 0)            arguments.put("passClassCrunchIntsFix255",                Collections.nCopies(255 - 1 /* (handle) */, 0));        final Consumer<Method> announcer = ("Linux".equalsIgnoreCase(                    System.getProperty("os.name", "")))            ? method -> System.err.format("\033[7m%s\033[0m%n",                            method.getName())            : method -> System.err.println(method.getName());        for (Method method : ArityLimits.class.getDeclaredMethods()) {            final List<?> methodArgs = arguments.get(                            method.getName());            if (methodArgs == null)                continue;            invocable.invoke(method, methodArgs);            announcer.accept(method);        }    }    private interface Invocable    {        void invoke(Method method, List<?> args) throws Throwable;        static boolean warnTooManyArgs(Method method, List<?> args)        {            if (method.isVarArgs() || args.size() < 256)                return false;            System.err.println(method.getName()                    .concat(": too many arguments"));            return true;        }    }    private static final class CoreInvoker implements Invocable    {        private final Supplier<?> instanceer;        CoreInvoker(Supplier<?> instanceer)        {            this.instanceer = instanceer;        }        public void invoke(Method method, List<?> args)                    throws ReflectiveOperationException        {            if (Invocable.warnTooManyArgs(method, args))                return;            method.invoke((Modifier.isStatic(method.getModifiers()))                    ? null                    : instanceer.get(),                args.toArray());        }    }    private static final class HandleInvoker implements Invocable    {        private final Supplier<?> instanceer;        private final Lookup lookup;        HandleInvoker(Supplier<?> instanceer, Lookup lookup)        {            this.instanceer = instanceer;            this.lookup = lookup;        }        public void invoke(Method method, List<?> args) throws Throwable        {            // Here a handle shall check its parameter constraints.            final MethodHandle mh1 = lookup.unreflect(method);            if (Invocable.warnTooManyArgs(method, args))                return;            final MethodHandle mh2 = (Modifier.isStatic(                            method.getModifiers()))                ? mh1                : mh1.bindTo(instanceer.get());            mh2.invokeWithArguments(args);        }    }    void passCrunchLongsFix128(/* MethodHandle mh, ArityLimits al, */            long _001, long _002, long _003, long _004, long _005,            long _006, long _007, long _008, long _009, long _010,            long _011, long _012, long _013, long _014, long _015,            long _016, long _017, long _018, long _019, long _020,            long _021, long _022, long _023, long _024, long _025,            long _026, long _027, long _028, long _029, long _030,            long _031, long _032, long _033, long _034, long _035,            long _036, long _037, long _038, long _039, long _040,            long _041, long _042, long _043, long _044, long _045,            long _046, long _047, long _048, long _049, long _050,            long _051, long _052, long _053, long _054, long _055,            long _056, long _057, long _058, long _059, long _060,            long _061, long _062, long _063, long _064, long _065,            long _066, long _067, long _068, long _069, long _070,            long _071, long _072, long _073, long _074, long _075,            long _076, long _077, long _078, long _079, long _080,            long _081, long _082, long _083, long _084, long _085,            long _086, long _087, long _088, long _089, long _090,            long _091, long _092, long _093, long _094, long _095,            long _096, long _097, long _098, long _099, long _100,            long _101, long _102, long _103, long _104, long _105,            long _106, long _107, long _108, long _109, long _110,            long _111, long _112, long _113, long _114, long _115,            long _116, long _117, long _118, long _119, long _120,            long _121, long _122, long _123, long _124, long _125,            long _126) { }    static void passClassCrunchLongsFix128(/* MethodHandle mh, */            long _001, long _002, long _003, long _004, long _005,            long _006, long _007, long _008, long _009, long _010,            long _011, long _012, long _013, long _014, long _015,            long _016, long _017, long _018, long _019, long _020,            long _021, long _022, long _023, long _024, long _025,            long _026, long _027, long _028, long _029, long _030,            long _031, long _032, long _033, long _034, long _035,            long _036, long _037, long _038, long _039, long _040,            long _041, long _042, long _043, long _044, long _045,            long _046, long _047, long _048, long _049, long _050,            long _051, long _052, long _053, long _054, long _055,            long _056, long _057, long _058, long _059, long _060,            long _061, long _062, long _063, long _064, long _065,            long _066, long _067, long _068, long _069, long _070,            long _071, long _072, long _073, long _074, long _075,            long _076, long _077, long _078, long _079, long _080,            long _081, long _082, long _083, long _084, long _085,            long _086, long _087, long _088, long _089, long _090,            long _091, long _092, long _093, long _094, long _095,            long _096, long _097, long _098, long _099, long _100,            long _101, long _102, long _103, long _104, long _105,            long _106, long _107, long _108, long _109, long _110,            long _111, long _112, long _113, long _114, long _115,            long _116, long _117, long _118, long _119, long _120,            long _121, long _122, long _123, long _124, long _125,            long _126, long _127) { }    void passCrunchIntsFix255(/* MethodHandle mh, ArityLimits al, */            int _001, int _002, int _003, int _004, int _005,            int _006, int _007, int _008, int _009, int _010,            int _011, int _012, int _013, int _014, int _015,            int _016, int _017, int _018, int _019, int _020,            int _021, int _022, int _023, int _024, int _025,            int _026, int _027, int _028, int _029, int _030,            int _031, int _032, int _033, int _034, int _035,            int _036, int _037, int _038, int _039, int _040,            int _041, int _042, int _043, int _044, int _045,            int _046, int _047, int _048, int _049, int _050,            int _051, int _052, int _053, int _054, int _055,            int _056, int _057, int _058, int _059, int _060,            int _061, int _062, int _063, int _064, int _065,            int _066, int _067, int _068, int _069, int _070,            int _071, int _072, int _073, int _074, int _075,            int _076, int _077, int _078, int _079, int _080,            int _081, int _082, int _083, int _084, int _085,            int _086, int _087, int _088, int _089, int _090,            int _091, int _092, int _093, int _094, int _095,            int _096, int _097, int _098, int _099, int _100,            int _101, int _102, int _103, int _104, int _105,            int _106, int _107, int _108, int _109, int _110,            int _111, int _112, int _113, int _114, int _115,            int _116, int _117, int _118, int _119, int _120,            int _121, int _122, int _123, int _124, int _125,            int _126, int _127, int _128, int _129, int _130,            int _131, int _132, int _133, int _134, int _135,            int _136, int _137, int _138, int _139, int _140,            int _141, int _142, int _143, int _144, int _145,            int _146, int _147, int _148, int _149, int _150,            int _151, int _152, int _153, int _154, int _155,            int _156, int _157, int _158, int _159, int _160,            int _161, int _162, int _163, int _164, int _165,            int _166, int _167, int _168, int _169, int _170,            int _171, int _172, int _173, int _174, int _175,            int _176, int _177, int _178, int _179, int _180,            int _181, int _182, int _183, int _184, int _185,            int _186, int _187, int _188, int _189, int _190,            int _191, int _192, int _193, int _194, int _195,            int _196, int _197, int _198, int _199, int _200,            int _201, int _202, int _203, int _204, int _205,            int _206, int _207, int _208, int _209, int _210,            int _211, int _212, int _213, int _214, int _215,            int _216, int _217, int _218, int _219, int _220,            int _221, int _222, int _223, int _224, int _225,            int _226, int _227, int _228, int _229, int _230,            int _231, int _232, int _233, int _234, int _235,            int _236, int _237, int _238, int _239, int _240,            int _241, int _242, int _243, int _244, int _245,            int _246, int _247, int _248, int _249, int _250,            int _251, int _252, int _253) { }    static void passClassCrunchIntsFix255(/* MethodHandle mh, */            int _001, int _002, int _003, int _004, int _005,            int _006, int _007, int _008, int _009, int _010,            int _011, int _012, int _013, int _014, int _015,            int _016, int _017, int _018, int _019, int _020,            int _021, int _022, int _023, int _024, int _025,            int _026, int _027, int _028, int _029, int _030,            int _031, int _032, int _033, int _034, int _035,            int _036, int _037, int _038, int _039, int _040,            int _041, int _042, int _043, int _044, int _045,            int _046, int _047, int _048, int _049, int _050,            int _051, int _052, int _053, int _054, int _055,            int _056, int _057, int _058, int _059, int _060,            int _061, int _062, int _063, int _064, int _065,            int _066, int _067, int _068, int _069, int _070,            int _071, int _072, int _073, int _074, int _075,            int _076, int _077, int _078, int _079, int _080,            int _081, int _082, int _083, int _084, int _085,            int _086, int _087, int _088, int _089, int _090,            int _091, int _092, int _093, int _094, int _095,            int _096, int _097, int _098, int _099, int _100,            int _101, int _102, int _103, int _104, int _105,            int _106, int _107, int _108, int _109, int _110,            int _111, int _112, int _113, int _114, int _115,            int _116, int _117, int _118, int _119, int _120,            int _121, int _122, int _123, int _124, int _125,            int _126, int _127, int _128, int _129, int _130,            int _131, int _132, int _133, int _134, int _135,            int _136, int _137, int _138, int _139, int _140,            int _141, int _142, int _143, int _144, int _145,            int _146, int _147, int _148, int _149, int _150,            int _151, int _152, int _153, int _154, int _155,            int _156, int _157, int _158, int _159, int _160,            int _161, int _162, int _163, int _164, int _165,            int _166, int _167, int _168, int _169, int _170,            int _171, int _172, int _173, int _174, int _175,            int _176, int _177, int _178, int _179, int _180,            int _181, int _182, int _183, int _184, int _185,            int _186, int _187, int _188, int _189, int _190,            int _191, int _192, int _193, int _194, int _195,            int _196, int _197, int _198, int _199, int _200,            int _201, int _202, int _203, int _204, int _205,            int _206, int _207, int _208, int _209, int _210,            int _211, int _212, int _213, int _214, int _215,            int _216, int _217, int _218, int _219, int _220,            int _221, int _222, int _223, int _224, int _225,            int _226, int _227, int _228, int _229, int _230,            int _231, int _232, int _233, int _234, int _235,            int _236, int _237, int _238, int _239, int _240,            int _241, int _242, int _243, int _244, int _245,            int _246, int _247, int _248, int _249, int _250,            int _251, int _252, int _253, int _254) { }}

This is an argument file args:

-XX:+UnlockExperimentalVMOptions-XX:+UseEpsilonGC-XX:+AlwaysPreTouch-Xms136m-Xmx136m-Xlog:heap*=info,gc=info

Save the files in, for example, a /tmp directory, and start a Java 11 version Docker container:

docker run --entrypoint /bin/sh --interactive \  --rm --tty --volume="/tmp:/tmp" \  --workdir="/tmp" eclipse-temurin:11-jdk-alpine    # It may pull in ~200 MiB.javac -Xdiags:verbose -Xlint ArityLimits.javajava @args ArityLimits handle $(( 1 | 2 | 4 | 8 ))  # 91/136 MiB of heap usedjava @args ArityLimits handle 8                     # 36/136 (see title)java @args ArityLimits core $(( 1 | 2 | 4 | 8 ))    #  1/136java @args ArityLimits core 8                       #  1/136rm *.classexit

Now, start a Java 17 version Docker container:

docker run --entrypoint /bin/sh --interactive \  --rm --tty --volume="/tmp:/tmp" \  --workdir="/tmp" eclipse-temurin:17-jdk-alpine    # It may pull in ~200 MiB.javac -Xdiags:verbose -Xlint ArityLimits.javajava @args ArityLimits handle $(( 1 | 2 | 4 | 8 ))  #  5/136 MiB of heap usedjava @args ArityLimits handle 8                     #  3/136 (see title)java @args ArityLimits core $(( 1 | 2 | 4 | 8 ))    #  1/136java @args ArityLimits core 8                       #  1/136rm *.classexit

Viewing latest article 9
Browse Latest Browse All 36

Trending Articles