Lite_stream
@Lite_stream

Closure Vs Common Function pointer?

Есть ли разница между obj.func(this::someMethod) vs obs.func(lambda / anonymous class) по памяти?
Ведь в случае со ссылкой на метод нельзя обращаться к переменным снаружи метода(замыкание), а в случае лямбы / анонимного класса - можно
  • Вопрос задан
  • 80 просмотров
Решения вопроса 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Java
Седой и строгий
Если лямбда или анонимный класс не используют переменных из объемлющей области видимости, то компилятор не станет их захватывать и разницы со ссылкой на метод не будет. Чтобы убедиться в этом, проведём эксперимент. Нам понадобится три класса:
import java.util.function.Supplier;

public class ExampleWithMethodRef {
    private static String someMethod() {
        return "Internal value";
    }

    public static void main(String[] args) {
        String value = "External value";
        Supplier<String> lambda = ExampleWithMethodRef::someMethod;
    }
}

import java.util.function.Supplier;

public class ExampleWithLambda {
    public static void main(String[] args) {
        String value = "External value";
        Supplier<String> lambda = () -> "Internal value";
    }
}

import java.util.function.Supplier;

public class ExampleWithCapturingLambda {
    public static void main(String[] args) {
        String value = "External value";
        Supplier<String> lambda = () -> value;
    }
}

Скомпилируем все три и запустим с параметром -Djdk.internal.lambda.dumpProxyClasses=. В рабочем каталоге появятся классы, в которые разворачиваются лямбды и ссылки на методы. Посмотрим, что у них внутри:

javap -p -c ExampleWithMethodRef$$Lambda$1
final class ExampleWithMethodRef$$Lambda$1 implements java.util.function.Supplier {                       
  private ExampleWithMethodRef$$Lambda$1();                                                               
    Code:                                                                                                 
       0: aload_0                                                                                         
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V                       
       4: return                                                                                          
                                                                                                          
  public java.lang.Object get();                                                                          
    Code:                                                                                                 
       0: invokestatic  #19                 // Method ExampleWithMethodRef.someMethod:()Ljava/lang/String;
       3: areturn                                                                                         
}


javap -p -c ExampleWithLambda$$Lambda$1
final class ExampleWithLambda$$Lambda$1 implements java.util.function.Supplier {                          
  private ExampleWithLambda$$Lambda$1();                                                                  
    Code:                                                                                                 
       0: aload_0                                                                                         
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V                       
       4: return                                                                                          
                                                                                                          
  public java.lang.Object get();                                                                          
    Code:                                                                                                 
       0: invokestatic  #19                 // Method ExampleWithLambda.lambda$main$0:()Ljava/lang/String;
       3: areturn                                                                                         
}


javap -p -c ExampleWithCapturingLambda$$Lambda$1
final class ExampleWithCapturingLambda$$Lambda$1 implements java.util.function.Supplier {                                            
  private final java.lang.String arg$1;
                                                                                                                                     
  private ExampleWithCapturingLambda$$Lambda$1(java.lang.String);                                                                    
    Code:                                                                                                                            
       0: aload_0                                                                                                                    
       1: invokespecial #13                 // Method java/lang/Object."<init>":()V                                                  
       4: aload_0                                                                                                                    
       5: aload_1                                                                                                                    
       6: putfield      #15                 // Field arg$1:Ljava/lang/String;                                                        
       9: return                                                                                                                     
                                                                                                                                     
  private static java.util.function.Supplier get$Lambda(java.lang.String);                                                           
    Code:                                                                                                                            
       0: new           #2                  // class ExampleWithCapturingLambda$$Lambda$1                                            
       3: dup                                                                                                                        
       4: aload_0                                                                                                                    
       5: invokespecial #19                 // Method "<init>":(Ljava/lang/String;)V                                                 
       8: areturn                                                                                                                    
                                                                                                                                     
  public java.lang.Object get();                                                                                                     
    Code:                                                                                                                            
       0: aload_0                                                                                                                    
       1: getfield      #15                 // Field arg$1:Ljava/lang/String;                                                        
       4: invokestatic  #28                 // Method ExampleWithCapturingLambda.lambda$main$0:(Ljava/lang/String;)Ljava/lang/String;
       7: areturn                                                                                                                    
}


Невооружённым глазом видно, что байткод вариантов со ссылкой на метод и лямбдой, не использующей переменную, идентичен, в обоих случаях происходит обращение в пул констант. Но если лямбда обращается к переменной, то в прокси-классе появляется поле arg$1, содержащее значение захватываемой переменной. Формально это влияет на потребление памяти, но если вы не собираетесь генерировать лямбды миллионами, то это влияние которым можно пренебречь, тем более во времена, когда даже на мобильных устройствах объёмы памяти измеряются гигабайтами.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы