3

I wrote this class that can be used to build Arrays of type T using a builder pattern, storing the values inside closures until the array is actually constructed.

public class ArrayBuilder<T> {

    final Class<T> type;

    public ArrayBuilder(Class<T> type){
        this.type = type;
    }

    private Supplier<Supplier<Store>> start = () -> {
        final Store element = new Store(-1, null, null);
        return () -> element;
    };

    private class Store {
        final Integer index;
        final T val;
        final Supplier<Store> getNextVal;

        public Store(Integer index, T val, Supplier<Store> getNextVal) {
            this.index = index;
            this.val = val;
            this.getNextVal = getNextVal;
        }
    }

    private Supplier<Store> queue(Integer index, T value, Supplier<Store> next) {
        final Store element = new Store(index, value, next);
        return () -> element;
    }

    public ArrayBuilder<T> add(T element) {
        Supplier<Store> currentStore = start.get();
        Integer currentIndex = start.get().get().index + 1;
        start = () -> queue(currentIndex, element, currentStore);
        return this;
    }

    public T[] build() {
        Store nextVal = start.get().get();
        Integer size = nextVal.index + 1;
        T[] result =  makeGenericArray(size);
        while (nextVal.index != -1) {
           result[nextVal.index] = nextVal.val;
           nextVal = nextVal.getNextVal.get();
        }
        return result;
    }

    private T[] makeGenericArray(Integer size) {
        return (T[]) Array.newInstance(type, size);
    }

}

This works quite well, but I was wondering where(stack?, heap?) the values are stored until build() gets called? Is there any reason this shouldn't be usable or performant? It does use reflections but that cost is paid only at the very end when build() is called.

Raudbjorn
  • 432
  • 2
  • 7
  • Heap. "It does use reflections" what makes you say that? – weston Apr 15 '17 at 20:06
  • sorry, imports are missing. return (T[]) java.lang.reflect.Array.newInstance(type, size);, in the last method – Raudbjorn Apr 15 '17 at 20:08
  • That's not actually reflecting anything. – weston Apr 15 '17 at 20:10
  • Wow, really? That's interesting. I'm obviously not very well informed about how that works. I'll have a look at the docs. Thank you for pointing that out. :) – Raudbjorn Apr 15 '17 at 20:12
  • Well, I actually don't know why `Array.newInstance` takes the type. Logic would say it doesn't need it as you could do this: `(T[])new Object[size];` – weston Apr 15 '17 at 20:18
  • 1
    OK, reading comments here: http://stackoverflow.com/a/2924453/360211 that ^ is not a great idea and it's more that there are runtime reasons an array should know it's actual type. I don't *think* that `newInstance` reflects over the type in anyway, e.g. fetching field or method definitions. – weston Apr 15 '17 at 20:20
  • 1
    It does appear to have zero to little cost. See: [here](http://stackoverflow.com/questions/30306160/performance-of-java-lang-reflect-array/#answer-30313347) Thank you for all your input @weston. :) – Raudbjorn Apr 15 '17 at 20:41

1 Answers1

7

Well, to be precise both heap and stack are involved in the lambda/closure construction process. To build the mind model of the closure you can think of it as creating an instance of a class for each lambda occurrence and passing to the constructor of that class all variables from the parent scopes which are accessed by the lambda. However, lets try to go through an example to see what exactly JVM does when building a closure for a lambda :



    public void performLambdasDemo() {
            // Declare variables which are going to be used in the lambda closure
            final Pair methodPairIntegerValue = new Pair(RandomUtils.nextInt(), RandomUtils.nextInt());
            final Integer methodIntegerValue = RandomUtils.nextInt();
            // Declare lambda
            final Supplier methodSupplierLambda = () -> {
                return methodPairIntegerValue.fst + 9000 + methodIntegerValue.intValue();
            };
            // Declare anonymous class
            final Supplier methodSupplierInnerClass = new Supplier() {
                @Override
                public Integer get() {
                    return methodPairIntegerValue.fst + 9001 + methodIntegerValue.intValue();
                }
            };
            System.out.println(methodSupplierLambda.get());
            System.out.println(methodSupplierInnerClass.get());
        }

What this useless code does is actually building an instance of a lambda and anonymous inner class doing exactly same. Now lets go through the corresponding byte code for both.

Lambdas

Below is the byte-code generated for the lambda :



    L2
        LINENUMBER 35 L2
        ALOAD 1
        ALOAD 2
        INVOKEDYNAMIC get(Lcom/sun/tools/javac/util/Pair;Ljava/lang/Integer;)Ljava/util/function/Supplier; [
          // handle kind 0x6 : INVOKESTATIC
          java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
          // arguments:
          ()Ljava/lang/Object;, 
          // handle kind 0x6 : INVOKESTATIC
          com/sfl/stackoverflow/LambdasExperiment.lambda$performLambdasDemo$1(Lcom/sun/tools/javac/util/Pair;Ljava/lang/Integer;)Ljava/lang/Integer;, 
          ()Ljava/lang/Integer;
        ]
        ASTORE 3
       L3
    // Omit quite some byte-code and jump to the method declaration
    // access flags 0x100A
      private static synthetic lambda$performLambdasDemo$1(Lcom/sun/tools/javac/util/Pair;Ljava/lang/Integer;)Ljava/lang/Integer;
       L0
        LINENUMBER 36 L0
        ALOAD 0
        GETFIELD com/sun/tools/javac/util/Pair.fst : Ljava/lang/Object;
        CHECKCAST java/lang/Integer
        INVOKEVIRTUAL java/lang/Integer.intValue ()I
        SIPUSH 9000
        IADD
        ALOAD 1
        INVOKEVIRTUAL java/lang/Integer.intValue ()I
        IADD
        INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
        ARETURN
        MAXSTACK = 2
        MAXLOCALS = 2

Despite being written in Java byte-code the code above is quite self explaining :


    ALOAD 1
    ALOAD 2

These two commands push the references methodPairIntegerValue and methodIntegerValue to the stack (here is were stack part comes in). This is followed by the INVOKEDYNAMIC command. This command is the main differentiating factor of the lambdas from the anonymous inner classes. If for the anonymous inner classes an explicit new class in generated in the byte-code, for lambdas the actual implementation is postponed till the runtime of the application. However, most modern JVMs when spotting INVOKEDYNAMIC generate a new class which has two properties capturing the values pushed to the stack prior the INVOKEDYNAMIC and create a new instance of it (and here where extra heap usage jumps in). It is worth mentioning that these actions are not directly performed by the INVOKEDYNAMIC but rather by LambdaMetafactory to which the call is being delegated to. So the end output is quite similar as it would have been for the anonymous inner class (JVMs are free to change this implementation detail incorporated by LambdaMetafactory in the future).



    private static synthetic lambda$performLambdasDemo$1(Lcom/sun/tools/javac/util/Pair;Ljava/lang/Integer;)Ljava/lang/Integer;

This is a static method containing actual code of the lambda expression. It is going to be invoked by a class LambdaMetafactory generates during the INVOKEDYNAMIC call. As you see what it does is pulling 2 values from the stack and performing actual summation.

Anonymous classes

Bellow is the byte-code for the usage of the anonymous class, things are simpler here, hence I have added only the initiation part of the anonymous class and omitted the byte-code for the actual class:



    L3
        LINENUMBER 39 L3
        NEW com/sfl/stackoverflow/LambdasExperiment$2
        DUP
        ALOAD 0
        ALOAD 1
        ALOAD 2
        INVOKESPECIAL com/sfl/stackoverflow/LambdasExperiment$2. (Lcom/sfl/stackoverflow/LambdasExperiment;Lcom/sun/tools/javac/util/Pair;Ljava/lang/Integer;)V
        ASTORE 4
       L4

What the code does is pushing the values of this, methodPairIntegerValue, methodIntegerValue to the stack and invoking the constructor of a anonymous class which captures these values in the fields of the anonymous class.

As it is seen from the code snippets above memory footprint wise lambdas and anonymous inner classes are quite similar.

Summary

Coming back to your question : The references used in the closure are passed around using the stack. The instance of the generated anonymous class together with its fields holding the references of the variables used in the closure are stored in the heap (as it would if you would explicitly use a class instead of lambda and pass the values via a constructor).

However, there is some difference in the performance of lambdas and anonymous inner classes with regards the bootstrap process and JIT. Following links cover the topic in a great detail:

Hope this helps (despite the answer being a bit lengthy)

Ruben
  • 761
  • 5
  • 9