Java - SAM类型优化
A working document描述Project Lambda的状态提到了所谓的SAM(单抽象方法)类型。据我所知,当前的lambda提议不会影响运行时,只需编译器就可以将lambda表达式自动转换为这些类型。Java - SAM类型优化
我认为在理想情况下,SAM类型的实例可以由函数指针在内部表示。因此,JVM可以避免为这些实例分配内存。
我想知道现代虚拟机是否能够提供这样的优化。
@陶你或许应该有Brian Goetz撰写该邮件列表后读:
http://mail.openjdk.java.net/pipermail/lambda-dev/2011-August/003877.html
基本上,拉姆达抽象使用对象目前实施。然而,它的目的是允许lambda的替代实现,这将比类的实例“小”。
您可以将情况想象成类似于自动装箱 - 将整数装入整数,但具有“较小”表示(如整数)。
目前,lambdas必须装箱到SAM类型的实例,b/c JVM目前无法用任何较小的构造表示lambda。将来,可能会有一个新的JVM标准,其中包括可以将lambda表示为对象之外的其他东西的“原始函数”。因此,要回答你的问题,上面提出的优化类型可能是可能的,但它可能会与Java 8之后的“原始函数”一起工作,而不是实现特定的功能。
将单个方法类转换为函数指针并没有什么困难,但是您错过了一件事情:lambda表达式不仅仅是函数,它们是闭包。区别在于闭包可以捕获外部变量。考虑在伪爪哇下例如:
public Adder makeAdder(double startNumber) {
return #{ int number -> number + startNumber}
}
...
int startNumber = 5;
Adder add5 = makeAdder(startNumber);
add5.invoke(4); // ==> 9
在这个例子中lambda函数,由呼叫产生的makeAdder(),是指被本拉姆达之外定义的变量。这就是为什么它被称为“闭包” - 它们“关闭”了它们的自由变量(在这种情况下 - 超过了startNumber)。为了处理这种情况,闭包必须同时持有指向函数的指针和指向其环境的指针。所以,你会得到一些有一个方法和至少一个变量的数据结构。但是它不是OOP中对象的定义吗?那么如果你可以将它作为匿名类的实例,那么创建新类型对象的原因是什么?
尽管如此,还是可以对这种匿名类进行一些其他优化。你指出的工作文档中提到了其中的一些,例如,推断和有效地使用最终变量(虽然这主要是为了允许JVM上的lambda表达式而不是优化代码)。生成的匿名类也可能是最终的,大多数JVM对最终的变量和类都有很好的优化。
其他改进也可能涉及到环境的参考 - 那里有很多选项。
谢谢你的回答!我唯一担心的是这些对象的内存分配,这可能会限制并发性,因为据我所知每个内存分配都涉及许多同步。当然我知道管理可重用对象是程序员的责任,我只想知道JVM是否能够避免为这些对象分配内存。 –
@Tamás:你能指出内存分配期间关于同步的资源吗?我无法找到关于此主题的任何信息,但我相信JVM中的默认内存分配不会使用“synchronized”之类的东西:现代VM中的单线程分配只需要一个分配给引用和一个指针增量。我相信多线程内存分配中的线程安全是由一些非常低级的构造完成的,可能基于处理器的原子操作,因此速度也非常快。如果是这样,那么在创建闭包时无需担心同步。 – ffriend
@TamásHotspot在内部使用TLS(线程本地存储)缓冲区,并且只有这些缓冲区在全局堆中分配完全时才会发生同步开销,但这并不是那么昂贵 - 在正常情况下它只是一个指针增量我不知道,但几乎肯定用CAS循环来实现)。 – Voo
当你说“函数指针”时,你是指指向函数代码开始的简单指针吗? – ffriend
@朋友 - 是的,我喜欢。我知道这种方法存在一些问题,例如可以在一个对象上进行同步。但是JVM也可以做一些其他非平凡的优化,例如内联虚拟方法。 –