- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我编写了此类,可用于使用构建器模式构建类型 T 的数组,将值存储在闭包中,直到实际构建数组为止。
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);
}
}
这工作得很好,但我想知道在调用 build() 之前,这些值存储在哪里(堆栈?,堆?)?有什么理由这不应该可用或性能不佳吗?它确实使用了反射,但只有在最后调用 build() 时才会支付该费用。
最佳答案
嗯,准确地说,堆和栈都参与了 lambda/闭包构造过程。要构建闭包的思维模型,您可以将其视为为每个 lambda 事件创建一个类的实例,并将 lambda 访问的父作用域中的所有变量传递给该类的构造函数。但是,让我们尝试通过一个示例来了解 JVM 在为 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());
}
这段无用的代码所做的实际上是构建一个 lambda 和匿名内部类的实例,其作用完全相同。现在让我们看一下两者的相应字节码。
Lambdas
下面是为 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
尽管是用 Java 字节码编写的,但上面的代码很容易 self 解释:
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;
这是一个静态方法,包含 lambda 表达式的实际代码。它将由 LambdaMetafactory
在 INVOKEDYNAMIC
调用期间生成的类调用。如您所见,它所做的是从堆栈中提取 2 个值并执行实际求和。
匿名类
下面是匿名类的使用字节码,这里比较简单,所以我只添加了匿名类的初始化部分,省略了实际类的字节码:
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
代码所做的是将 this
、methodPairIntegerValue
、methodIntegerValue
的值压入堆栈并调用匿名类的构造函数在匿名类的字段中捕获这些值。
从上面的代码片段可以看出,内存占用明智的 lambda 和匿名内部类非常相似。
总结
回到你的问题:闭包中使用的引用使用堆栈 传递。生成的匿名类的实例及其保存闭包中使用的变量引用的字段存储在堆中(如果您显式使用类而不是 lambda 并传递通过构造函数的值)。
但是,在引导过程和 JIT 方面,lambda 和匿名内部类的性能存在一些差异。以下链接详细介绍了该主题:
希望这会有所帮助(尽管答案有点冗长)
关于java - Java 8 闭包存储在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43430682/
以下闭包函数在 javascript 中运行良好。 function generateNextNumber(startNumber) { var current = startNumber;
Swift的闭包(Closures)是一种将功能块和上下文整合并演示在代码中的一种手段。闭包可以捕获并存储其上下文中的变量和常量。与普遍存在于其他语言的匿名函数(如Python的lambda、Java
在本教程中,您将借助示例了解 JavaScript 闭包。 在了解闭包之前,您需要了解两个概念: 嵌套函数 返回函数 JavaScript 嵌套函数 在 JavaScript 中,一个函数也可
在本教程中,您将借助示例了解 JavaScript 闭包。 在了解闭包之前,您需要了解两个概念: 嵌套函数 返回函数 JavaScript 嵌套函数 在 JavaScript 中,一个函数也可
闭包介绍 闭包(closure)是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现。 要理解闭包,首先必须理解Javascript特殊的变量作用域。 1.全局变量和局部变
这个问题已经有答案了: Methods in ES6 objects: using arrow functions (6 个回答) 已关闭 6 年前。 我已经在 stackoverflow 上到处查找
这个问题已经有答案了: How do JavaScript closures work? (86 个回答) 已关闭 9 年前。 我有一个关于 Javascript 闭包的简单问题: 给出了以下函数:
所以我有以下内容: Object a = data.getA(); Object b = data.getB(); Object c = data.getC(); // and so on 这些对象是
现在已经很晚了,我大脑中道格拉斯·克罗克福德居住的部分已经关闭。我尝试了一些方法,但没有达到预期效果。 我有一个 Canvas ,我在其中画了两条线,然后在计时器上淡出它们,但只有循环中的最后一行淡出
因此,我创建了一个变量 car,然后将其分配给一个函数并添加了参数模型、年份。然后在函数内引用参数创建一个对象。 然后创建“闭包”内部函数 yourCar() 并返回其中的外部函数对象“Propert
我正在 Mozilla 开发者网站上阅读关于关闭的解释,并且有点挣扎。请查看 Mozilla 网站上的以下代码。我有点理解它是如何工作的,但我认为我的评论下面的代码也应该工作。为什么一点击18、20就
这个问题在这里已经有了答案: UnboundLocalError trying to use a variable (supposed to be global) that is (re)assig
以下程序返回“本地”,根据我正在阅读的教程,它旨在演示闭包现象` 我不明白的是,为什么最后为了调用parentfunction,将其分配给变量“child”,然后调用“child”。 为什么只写 pa
我读到闭包末尾的()会立即执行。那么,这两者之间有什么区别。我在一些代码中看到了第一个用法。 谢谢。 for (var a=selectsomeobj(),i=0,len=a.length;i
代码如下 var collection = (function (){ var x = 0; return {
我仍然对 JavaScript 中的闭包概念感到困惑。我明白闭包是内部函数在母函数返回后访问在其母函数中创建的变量的能力。但是我仍然很困惑,如果我们可以在函数内部创建一个变量,为什么我们必须创建内部函
我搜索了很多主题并没有找到答案,或者问题太复杂了。所以没关系。这是我的第一个问题。 这是 SQL SELECT parent.*, ( SELECT COUNT(*) FROM
有 JS 高手可以解释为什么会这样吗: $$={} (function(x){ x.newModule = { func: function(){...} };
在此示例中,我尝试按值传递,但传递的是引用。 for (int i = 0; i new PhoneJobTest(i); t.Start(); } 这可以像这样补救: for (int
从 $.each() 中访问 this.rules 变量的最佳方式是什么?任何关于原因/方式的解释也会有帮助! app.Style = function(node) { this.style
我是一名优秀的程序员,十分优秀!