什么是capturing lambda

​

先说下capturing lambda的影响:

在循环里面使用lambda函数造成捕获性lambda后,会产生多余的引用,从而使程序进行原来本可以避免的垃圾回收。从效率的角度上看,这是我们应该避免的。

接下来我们看看什么是capturing lambda。

在学习Disruptor(高性能队列)的时候,它的官方文档有这样一段话:(重点部分已加粗)

ini 复制代码
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++)
{
    bb.putLong(0, l);
    ringBuffer.publishEvent((event, sequence) -> event.set(bb.getLong(0)));
    Thread.sleep(1000);
}

This(指上面那段代码) would create a capturing lambda, meaning that it would need to instantiate an object to hold the ByteBuffer bb variable as it passes the lambda through to the publishEvent() call. This will create additional (unnecessary) garbage, so the call that passes the argument through to the lambda should be preferred if low GC pressure is a requirement.

从上面的代码我们可以看到在一个lambda函数 (event, sequence) -> event.set(bb.getLong(0) 里引用到了外部变量bb。那么实际上在虚拟机翻译的时候,首先会在lambda所代表的内部类里生成一个引用,这个引用在内部类构造的时候引用了外面的变量bb(lambda表达式其实就是一个匿名内部类)。

类似于以下代码(我们屏蔽掉disrutor的细节):

arduino 复制代码
        String bb;
        Functional a = new Functional() {
            private final String bb = InnerClass.this.bb;
            @Override
            public void test() {
                System.out.printf(bb);
            }
        };

也就是说在内部类里我们多了一个bb的引用副本,那么在循环的时候,循环多少次就会多几个不必要的bb引用副本。这也是为什么disruptor文档说"This will create additional (unnecessary) garbage",这会增加额外的不必要的垃圾。

那么怎么解决呢?

解决方式就是在lambda的外部类增加一个静态方法,类似于:

arduino 复制代码
    public static void translate(LongEvent event, long sequence, ByteBuffer buffer)
    {
        event.set(buffer.getLong(0));
    }
    public static void main(String[] args) throws Exception
    {
        ByteBuffer bb = ByteBuffer.allocate(8);
        for (long l = 0; true; l++)
        {
            bb.putLong(0, l);
            ringBuffer.publishEvent(LongEventMain::translate, bb);
            Thread.sleep(1000);
        }
    }

和原来的对比一下,方便体会:

ini 复制代码
ByteBuffer bb = ByteBuffer.allocate(8);
for (long l = 0; true; l++)
{
    bb.putLong(0, l);
    ringBuffer.publishEvent((event, sequence) -> event.set(bb.getLong(0)));
    Thread.sleep(1000);
}

​

相关推荐
budingxiaomoli2 小时前
Spring统一功能处理
java·后端·spring
Flynn Liang3 小时前
可观测性与成本治理:一次对话的 17 个观测点
java·langchain·可观测性·micrometer·langchain4j·token 成本·llm 监控
金銀銅鐵4 小时前
带您看懂字节码指令(三):同一行加法,为什么有四种指令
java·jvm
jyOverQ5 小时前
从零学 JVM(五):垃圾回收机制——对象存活判断与 GC Roots
jvm
文丑颜不良啊8 小时前
LeetCode2821~2840题目解析
java·leetcode
apcipot_rain8 小时前
C++转Java日记(10)
java·开发语言
简 洁 冬冬9 小时前
泛型方法写法全面辨析:从「能编译」到「能跑」到「能用」
java·泛型
LGL6030A9 小时前
Java学习历程31——初识Spring Boot
java·spring boot·学习
金銀銅鐵9 小时前
一个 4 个常量的枚举,Kotlin 在字节码里生成了什么
jvm·kotlin
Lansonli11 小时前
大数据Spark(八十八):Spark任务资源之Standalone集群测试
java·服务器·spark