Lua 上值(UpValue)

Lua 上值(UpValue)

一、核心结论

UpValue(上值)就是:闭包函数引用的、定义在它外部作用域中的变量。

例如:

lua 复制代码
function CreateCounter()
    local count = 0

    return function()
        count = count + 1
        return count
    end
end

这里内部匿名函数使用了外层的 count

虽然 CreateCounter() 执行结束后,count 按普通局部变量来说应该离开作用域,但这个变量仍然被返回的闭包使用。

这个 count 就是:

UpValue

可以简单理解成:

text 复制代码
外层局部变量
    ↓
被内部函数引用
    ↓
变成闭包的 UpValue
    ↓
外层函数结束后依然存活

二、为什么需要 UpValue?

主要是为了实现 Lua 的闭包(Closure)

例如:

lua 复制代码
function CreateCounter()
    local count = 0

    return function()
        count = count + 1
        return count
    end
end

local counter = CreateCounter()

print(counter()) -- 1
print(counter()) -- 2
print(counter()) -- 3

这里:

lua 复制代码
count

属于 CreateCounter 的局部变量。

CreateCounter() 已经执行完了,count 仍然需要保存。

因此 Lua 会让这个变量继续存活,并由闭包持有。


三、UpValue 与普通局部变量的区别

普通局部变量:

lua 复制代码
function Test()
    local a = 10
end

a 只在 Test 的生命周期内有效。

而 UpValue:

lua 复制代码
function Test()
    local a = 10

    return function()
        print(a)
    end
end

这里 a 被内部函数引用,因此它需要在外层函数结束后继续存在。

可以理解为:

text 复制代码
普通局部变量:

函数栈帧
┌──────────┐
│ a = 10   │
└──────────┘
函数结束 → 消失


UpValue:

函数栈帧
┌──────────┐
│ a = 10   │
└────┬─────┘
     │
     ▼
   UpValue
     ▲
     │
   Closure

四、UpValue 和 Closure 的关系

这是面试最容易问的一组概念。

Closure 是什么?

闭包可以简单理解成:

函数 + 它所引用的外部变量

例如:

lua 复制代码
function Create()
    local x = 100

    return function()
        return x
    end
end

可以理解为:

text 复制代码
Closure
├── Function
└── UpValue
      └── x = 100

所以:

UpValue 是 Closure 保存外部变量的机制。


五、UpValue 的一个重要特点:共享

这个知识点很容易出面试题。

例如:

lua 复制代码
function Create()
    local value = 0

    local function Add()
        value = value + 1
    end

    local function Get()
        return value
    end

    return Add, Get
end

然后:

lua 复制代码
local add, get = Create()

add()
add()

print(get()) -- 2

为什么 get() 能拿到 2

因为:

text 复制代码
Add
 ↓
共同引用
 ↓
UpValue(value)
 ↑
共同引用
 ↑
Get

Add 和 Get 共享的是同一个 UpValue,而不是各自复制了一份 value

这点在理解 Lua 闭包实现时非常重要。


六、UpValue 的"开放"和"闭合"

如果面试官问得比较深入,可能会问:

Lua 底层是怎么实现 UpValue 的?

可以简单分成两个状态:

1. Open UpValue

外层函数还在执行时:

text 复制代码
局部变量还在栈上
      ↓
UpValue 指向栈上的变量

例如:

text 复制代码
Stack
┌──────────┐
│ x = 100  │ ← UpValue 指向这里
└──────────┘

2. Closed UpValue

外层函数执行结束后:

text 复制代码
栈上的变量即将失效
        ↓
把变量复制到堆上的 UpValue
        ↓
Closure 继续引用

概念上:

text 复制代码
执行中:

Stack
  ↑
UpValue
  ↑
Closure


外层函数结束:

UpValue
  ↓
独立保存 x
  ↑
Closure

所以:

UpValue 可以理解为把外部局部变量的生命周期延长到闭包仍然需要它的时候。


七、为什么 Unity / Lua 项目要关注 UpValue?

主要是两个方面。

1. 闭包产生的额外开销

例如高频代码里:

lua 复制代码
for i = 1, 10000 do
    local callback = function()
        print(i)
    end
end

会不断创建闭包以及相关 UpValue,可能带来:

text 复制代码
内存分配
+
Lua GC 压力

所以在 Unity 游戏中,Update、循环、战斗高频逻辑里不要无意义地大量创建闭包。


2. UpValue 会影响对象生命周期

例如:

lua 复制代码
function Create()
    local hugeTable = CreateHugeTable()

    return function()
        return hugeTable
    end
end

只要返回的闭包还存在:

text 复制代码
Closure
  ↓
UpValue
  ↓
hugeTable

那么 hugeTable 仍然可能被引用。

因此:

一个闭包如果无意中持有大型对象,会导致对象不能及时被 GC 回收。

这在大型 Lua 项目中属于比较典型的内存问题。


八、面试高频追问

Q1:UpValue 是什么?

闭包引用的外层作用域中的变量,就是 UpValue。


Q2:UpValue 和 Closure 有什么关系?

Closure 是函数和相关环境的组合,UpValue 是 Closure 用来保存外部变量引用的机制。


Q3:为什么外层函数执行完,UpValue 还存在?

因为被闭包引用的局部变量生命周期被延长了。Lua 会让它脱离原来的栈生命周期,继续由 UpValue 持有。


Q4:多个闭包会共享 UpValue 吗?

会。同一个外层局部变量被多个闭包引用时,它们可以共享同一个 UpValue,因此一个闭包修改变量,另一个闭包也能看到修改结果。


Q5:UpValue 会造成 GC 问题吗?

会。大量创建闭包会增加 Lua 对象和 GC 压力;如果闭包长期持有大型 table 或对象,还可能延长这些对象的生命周期,导致内存占用增加。


面试记忆关键词

闭包 → 外部变量 → UpValue → 延长生命周期 → 多闭包共享 → Open/Closed → GC


  • 🎬 博客主页:https://xiaoy.blog.csdn.net

  • 🎥 本文由 呆呆敲代码的小Y 原创 🙉

  • 🎄 学习专栏推荐:Unity系统学习专栏

  • 🌲 游戏制作专栏推荐:游戏制作

  • 🌲Unity实战100例专栏推荐:Unity 实战100例 教程

  • 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

  • 📆 未来很长,值得我们全力奔赴更美好的生活✨

  • ------------------❤️分割线❤️-------------------------

资料白嫖,技术互助

学习路线指引(点击解锁) 知识定位 人群定位
🧡 Unity系统学习专栏 入门级 本专栏从Unity入门开始学习,快速达到Unity的入门水平
💛 Unity实战类项目 进阶级 计划制作Unity的 100个实战案例!助你进入Unity世界,争取做最全的Unity原创博客大全。
❤️ 游戏制作专栏 难度偏高 分享学习一些Unity成品的游戏Demo和其他语言的小游戏!
💚 游戏爱好者万人社区 互助/吹水 数万人游戏爱好者社区,聊天互助,白嫖奖品
💙 Unity100个实用技能 Unity查漏补缺 针对一些Unity中经常用到的一些小知识和技能进行学习介绍,核心目的就是让我们能够快速学习Unity的知识以达到查漏补缺
相关推荐
呆呆敲代码的小Y19 分钟前
【游戏开发】xLua 性能优化技巧
junit·性能优化·lua·xlua
呆呆敲代码的小Y20 分钟前
【游戏开发】C# 中的迭代器
java·开发语言·c#·迭代器
benchmark_cc22 分钟前
K 线数据断层会对回测造成什么影响?从一个缺失交易日看懂量化策略为什么会失真
大数据·开发语言·数据分析·量化·股票数据·quantdash·量化数据源
benchmark_cc28 分钟前
数据 API 稳定性为什么会影响量化策略?从数据获取到信号执行的完整分析
开发语言·python·数据分析·量化·股票数据·quantdash·量化数据源
曼巴UE539 分钟前
Unlua UE5.3 调试
ue5·lua·unlua
2601_9621803342 分钟前
python——Django 框架
开发语言·python·django
长江后浪博客42 分钟前
Conda环境下测试Intel NPU:Python版本如何选择?
开发语言·python·conda·openvino·intel npu
Chester_19991 小时前
CSP202206C.角色授权
开发语言·数据结构·c++·蓝桥杯
晴天的雨.9921 小时前
类和对象下(内部类,匿名对象,对象拷贝时的编译器优化)
开发语言·c++·算法