Go 1.25.5 通关讲解

作者:吴佳浩
最后更新:2025-12-24
适用版本:golang 1.25.5
距离上一次写go的版本更新已经一年多了快两年了,今天继续和大家一起来聊一聊go的最新版本都更新了些什么内容,内容比较多筒子们搬好小笨板凳,一起来学习一下吧!!!
介绍
Go 1.25 是在 2025 年 8 月发布的重要版本,紧随 Go 1.24 六个月后推出。Go 1.25.5 是该系列的最新补丁版本(发布于 2025 年 12 月 2 日),主要包含安全修复和 bug 修复。这一版本保持了 Go 1 的兼容性承诺,因此几乎所有的 Go 程序都能够像以前一样进行编译和运行。
核心特性概览
mindmap
root(("Go 1.25.5"))
运行时增强
容器感知 GOMAXPROCS
实验性垃圾回收器
nil 指针检查修复
标准库新增
testing/synctest 正式版
实验性 JSON v2
os.Root API 完善
工具链改进
"DWARF 5 调试信息"
"go doc http 服务器"
"go.mod ignore 指令"
性能优化
切片栈分配优化
编译器内联增强
安全修复
crypto/x509 修复
资源消耗限制
重大变更详解
1. 容器感知的 GOMAXPROCS
这是 Go 1.25 最重要的运行时改进之一!
问题背景
在 Go 1.25 之前,在容器环境中运行 Go 程序时会遇到一个严重问题:
go
// 假设你的 Kubernetes 容器分配了 4 个 CPU
// 但主机有 16 个 CPU 核心
func main() {
fmt.Println("GOMAXPROCS:", runtime.GOMAXPROCS(0))
// 在 Go 1.24 及之前: 输出 16 (错误!)
// 在 Go 1.25: 输出 4 (正确!)
}
新的行为
flowchart TD
A[程序启动] --> B{检查环境}
B -->|容器环境| C[读取 cgroup CPU 限制]
B -->|非容器| D[使用逻辑 CPU 数量]
C --> E{比较值}
E -->|CPU 限制 < 逻辑 CPU| F[使用较小值]
E -->|CPU 限制 >= 逻辑 CPU| D
F --> G[设置 GOMAXPROCS]
D --> G
G --> H[定期更新检查]
H --> I{限制变化?}
I -->|是| G
I -->|否| J[继续运行]
实际示例
go
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
fmt.Println("初始 GOMAXPROCS:", runtime.GOMAXPROCS(0))
fmt.Println("逻辑 CPU 数:", runtime.NumCPU())
// Go 1.25 会自动考虑 cgroup 限制
// 在 Docker/K8s 中:
// docker run --cpus=4 yourimage
// GOMAXPROCS 将自动设置为 4
// 如果需要手动设置
runtime.GOMAXPROCS(8)
fmt.Println("手动设置后:", runtime.GOMAXPROCS(0))
// 或使用新的 API 恢复默认值
runtime.SetDefaultGOMAXPROCS()
fmt.Println("恢复默认后:", runtime.GOMAXPROCS(0))
}
重要提示:
- 这个特性仅在
go.mod中指定 Go 1.25 或更高版本时才生效 - 可通过
GODEBUG=containermaxprocs=0禁用 - 可通过
GODEBUG=updatemaxprocs=0禁用自动更新
2. testing/synctest - 并发测试的救星
从实验性功能正式毕业!这个包解决了 Go 并发测试的老大难问题。
传统并发测试的痛点
go
// 传统方式 - 不可靠且慢
func TestOldWay(t *testing.T) {
ch := make(chan int)
go func() {
time.Sleep(100 * time.Millisecond) // 😱 不确定的等待
ch <- 42
}()
select {
case v := <-ch:
if v != 42 {
t.Errorf("期望 42, 得到 %d", v)
}
case <-time.After(1 * time.Second): // 😱 浪费时间
t.Fatal("超时")
}
}
使用 synctest 的新方式
go
package main
import (
"testing"
"testing/synctest"
"time"
)
func TestWithSynctest(t *testing.T) {
// 在虚拟时间"气泡"中运行测试
synctest.Test(t, func(t *testing.T) {
ch := make(chan int)
go func() {
time.Sleep(100 * time.Millisecond) // ⚡ 虚拟时间,瞬间完成!
ch <- 42
}()
// 等待所有 goroutine 阻塞
synctest.Wait()
v := <-ch
if v != 42 {
t.Errorf("期望 42, 得到 %d", v)
}
})
}
// 更复杂的例子 - 测试超时逻辑
func Read(input <-chan int) (int, error) {
select {
case v := <-input:
return v, nil
case <-time.After(1 * time.Minute):
return 0, fmt.Errorf("超时")
}
}
func TestReadTimeout(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
ch := make(chan int)
// 测试超时情况
_, err := Read(ch)
if err == nil {
t.Error("期望超时错误")
}
})
// ⚡ 1 分钟的超时在虚拟时间中瞬间完成!
}
synctest 的魔法:
sequenceDiagram
participant Test as 测试代码
participant Bubble as Synctest 气泡
participant Time as 虚拟时钟
participant Goroutines as Goroutines
Test->>Bubble: synctest.Test()
Bubble->>Time: 创建虚拟时钟
Test->>Goroutines: 启动 goroutines
Goroutines->>Time: time.Sleep(1min)
Note over Goroutines,Time: 所有 goroutine 阻塞
Test->>Bubble: synctest.Wait()
Bubble->>Time: 检测到全部阻塞
Time->>Time: ⚡ 瞬间推进时间!
Time->>Goroutines: 唤醒
Goroutines->>Test: 返回结果
3. 实验性 JSON v2 包
完全重写的 JSON 实现,解决老版本的诸多痛点!
启用方式
bash
GOEXPERIMENT=jsonv2 go build
新特性对比
go
// 旧版 encoding/json 的问题
type User struct {
Name string
Email string
}
// 无法自定义字段顺序
// 无法控制空值处理
// 性能较差
// 错误信息不够详细
// 使用 JSON v2 (实验性)
import "encoding/json/v2"
type User struct {
Name string `json:"name"`
Email string `json:"email,omitzero"` // 新标签!
}
func example() {
user := User{Name: "张三"}
// 更好的性能
// 更详细的错误信息
// 更灵活的配置选项
data, err := jsonv2.Marshal(user)
if err != nil {
// 错误信息更加友好和详细
fmt.Println(err)
}
}
JSON v2 改进:
- 更高性能
- 更精确的错误信息
- 更灵活的配置
- 更好的流式 API
- 注意:API 可能会在未来版本中调整
4. 实验性 Green Tea 垃圾回收器
针对现代多核系统和大量小对象场景优化的新 GC!
启用方式
bash
GOEXPERIMENT=greenteagc go build
GC 演进历程
timeline
title Go 垃圾回收器演进
Go 1.0-1.4 : 标记-清除 GC
: 停顿时间长
Go 1.5 : 并发标记-清除
: 大幅降低停顿
Go 1.8-1.24 : 持续优化
: 亚毫秒级停顿
Go 1.25 : Green Tea GC (实验)
: NUMA 优化
: 适合多核系统
Green Tea GC 特点
go
// Green Tea GC 针对以下场景优化:
// 1. 大量小对象创建
type Cache struct {
data map[string]*Entry
}
func (c *Cache) Process() {
// 频繁创建小对象
for i := 0; i < 1000000; i++ {
entry := &Entry{
Key: fmt.Sprintf("key-%d", i),
Value: []byte("data"),
}
c.data[entry.Key] = entry
}
}
// 2. 多核系统 (8+ 核心)
// Green Tea GC 减少内存跳转
// 在 NUMA 架构上表现更好
// 3. 高并发场景
// 更好的 CPU 缓存利用率
// 减少等待内存的延迟
使用建议:
- 适合:多核服务器、微服务、高并发应用
- ⚠️ 实验性质,API 和实现可能变化
- 建议先在测试环境验证性能
5. nil 指针检查修复 - 重要的 Bug 修复
Go 1.21-1.24 中存在一个编译器 bug,现在修复了!
Bug 演示
go
// Go 1.21-1.24 的 BUG 行为 (错误!)
package main
import (
"fmt"
"os"
)
func oldBuggyBehavior() {
f, err := os.Open("不存在的文件.txt")
// 危险!在检查错误前使用了 f
name := f.Name() // 在 Go 1.21-1.24 中不会 panic (BUG!)
if err != nil {
return
}
fmt.Println(name)
}
// Go 1.25 的正确行为
func fixedBehavior() {
f, err := os.Open("不存在的文件.txt")
// 正确:先检查错误
if err != nil {
fmt.Println("错误:", err)
return
}
// 只有在没有错误时才使用 f
name := f.Name()
fmt.Println(name)
}
// 在 Go 1.25 中会正确 panic
func willPanicNow() {
f, err := os.Open("不存在的文件.txt")
name := f.Name() // 💥 panic: nil pointer dereference
if err != nil {
return
}
fmt.Println(name)
}
迁移检查清单:
graph LR
A[审查代码] --> B{是否先使用
后检查错误?} B -->|是| C[ 需要修复] B -->|否| D[ 无需改动] C --> E[调整顺序:
先检查错误] E --> F[重新测试] F --> G[升级到 Go 1.25] D --> G
后检查错误?} B -->|是| C[ 需要修复] B -->|否| D[ 无需改动] C --> E[调整顺序:
先检查错误] E --> F[重新测试] F --> G[升级到 Go 1.25] D --> G
工具链改进
1. DWARF 5 调试信息
bash
# 默认使用 DWARF 5
go build -o myapp main.go
# 优势:
# 调试信息体积减少
# 链接速度更快(大型程序明显)
# 更好的调试器支持
# 如需禁用(回退到 DWARF 4)
GOEXPERIMENT=nodwarf5 go build -o myapp main.go
2. go doc 内置 HTTP 服务器
bash
# 新功能!启动本地文档服务器
go doc -http=:8080
# 浏览器访问 http://localhost:8080
# 查看所有包的文档
# 搜索功能
# 比 pkg.go.dev 更快
3. go.mod ignore 指令
go
// go.mod
module myproject
go 1.25
// 忽略特定目录
ignore ./vendor
ignore ./testdata
ignore ./tmp
// go 命令会忽略这些目录
标准库更新精选
1. net/http - CSRF 保护
go
package main
import (
"net/http"
)
func main() {
mux := http.NewServeMux()
// 注册处理器
mux.HandleFunc("/api/data", handleData)
// 使用新的 CrossOriginProtection
protection := &http.CrossOriginProtection{
// 允许的额外来源
AllowedOrigins: []string{
"https://trusted-domain.com",
},
}
// 应用保护
handler := protection.Wrap(mux)
http.ListenAndServe(":8080", handler)
}
func handleData(w http.ResponseWriter, r *http.Request) {
// 自动检查:
// Sec-Fetch-Site 头
// Origin 与 Host 比较
// 拒绝不安全的跨域请求
w.Write([]byte("安全的响应"))
}
2. sync.WaitGroup.Go 方法
go
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
// 老方法 - 啰嗦
wg.Add(1)
go func() {
defer wg.Done()
doWork()
}()
// 新方法 - 简洁! ✨
wg.Go(func() {
doWork()
})
// 多个任务
for i := 0; i < 5; i++ {
i := i // 注意:Go 1.22+ 不需要这行
wg.Go(func() {
fmt.Println("任务", i)
})
}
wg.Wait()
}
func doWork() {
time.Sleep(100 * time.Millisecond)
fmt.Println("工作完成")
}
3. os.Root - 安全的文件系统操作
go
package main
import (
"fmt"
"os"
)
func main() {
// 创建受限的文件系统根
root, err := os.OpenRoot("/safe/directory")
if err != nil {
panic(err)
}
defer root.Close()
// 所有操作都限制在 /safe/directory 内
// 读取文件
data, err := root.ReadFile("config.json")
// 创建目录
err = root.Mkdir("subdir", 0755)
// 符号链接 (Go 1.25 新增!)
err = root.Symlink("target.txt", "link.txt")
// 读取符号链接 (Go 1.25 新增!)
target, err := root.Readlink("link.txt")
// 递归删除 (Go 1.25 新增!)
err = root.RemoveAll("old-data")
// 无法访问父目录
// 即使尝试 "../../../etc/passwd" 也会被阻止
_, err = root.Open("../../../etc/passwd")
// err != nil (权限拒绝)
fmt.Println("安全操作完成")
}
4. runtime.FlightRecorder - 运行时分析
go
package main
import (
"fmt"
"os"
"runtime"
)
func main() {
// 启动 flight recorder
runtime.StartFlightRecorder()
// 运行你的程序
doWork()
// 发生问题时,获取记录
if err := detectProblem(); err != nil {
// 保存最近的运行时事件
f, _ := os.Create("flight-record.txt")
runtime.WriteFlightRecord(f)
f.Close()
fmt.Println("问题记录已保存")
}
}
func doWork() {
// 你的应用逻辑
for i := 0; i < 1000; i++ {
// ...
}
}
func detectProblem() error {
// 检测异常情况
return nil
}
性能优化
切片栈分配优化
go
// 编译器现在可以在栈上分配更多切片
func process() {
// 以前:在堆上分配
// 现在:可能在栈上分配(更快!)
data := make([]byte, 1024)
// 多个连续切片也能优化
batch1 := make([]int, 10)
batch2 := make([]int, 20)
batch3 := make([]int, 30)
// 使用切片...
_ = data
_ = batch1
_ = batch2
_ = batch3
}
// 性能提升:
// 减少 GC 压力
// 更好的缓存局部性
// 更快的内存访问
GOAMD64=v3 融合乘加指令
go
// 在 GOAMD64=v3 或更高版本
func calculate(a, b, c float64) float64 {
// 编译器会使用 FMA (Fused Multiply-Add) 指令
return a*b + c
// 优势:
// 更快(单指令)
// 更精确(更少舍入误差)
// 如需避免融合:
// return float64(a*b) + c
}
// 编译时启用:
// GOAMD64=v3 go build
安全更新 (Go 1.25.5 特有)
CVE-2025-61729: crypto/x509 资源消耗
go
// 修复前:恶意证书可导致资源耗尽
// HostnameError.Error() 无限制打印主机名
// 修复后:
// 限制打印的主机名数量
// 使用 strings.Builder 提高效率
// 防止二次时间复杂度
// 无需代码更改,升级到 1.25.5 即可
平台支持更新
Linux 平台增强
go
// linux/loong64 现在支持:
// race 检测器
// SetCgoTraceback
// 内部链接模式
// linux/riscv64 现在支持:
// plugin 构建模式
// GORISCV64 新值:
// GORISCV64=rva23u64 go build
macOS 要求
bash
# 重要:Go 1.25 需要 macOS 12 Monterey 或更高版本
# 不再支持 macOS 11 Big Sur 及更早版本
# 检查你的 macOS 版本:
sw_vers
# ProductName: macOS
# ProductVersion: 12.0 或更高
Windows 32位 ARM 弃用
bash
# ⚠️ windows/arm (32位) 将在 Go 1.26 中移除
# 如果你使用此平台,请计划迁移到:
# - windows/arm64 (推荐)
# - 其他平台
实验功能总结
graph TB
A[Go 1.25 实验性功能] --> B[GOEXPERIMENT=greenteagc]
A --> C[GOEXPERIMENT=jsonv2]
A --> D[GOEXPERIMENT=nodwarf5]
B --> B1[新垃圾回收器]
B --> B2[NUMA 优化]
B --> B3[多核性能提升]
C --> C1[重写的 JSON 包]
C --> C2[更好的性能]
C --> C3[API 可能变化]
D --> D1[回退到 DWARF 4]
D --> D2[兼容性选项]
style A fill:#f9f,stroke:#333,stroke-width:4px
style B fill:#ff9,stroke:#333
style C fill:#ff9,stroke:#333
style D fill:#9f9,stroke:#333
GODEBUG 设置一览
bash
# 容器相关
GODEBUG=containermaxprocs=0 # 禁用 cgroup CPU 限制感知
GODEBUG=updatemaxprocs=0 # 禁用 GOMAXPROCS 自动更新
# GC 调试
GODEBUG=checkfinalizers=1 # 启用 finalizer 诊断
# 其他
GODEBUG=http2client=0 # 禁用 HTTP/2 客户端
GODEBUG=http2server=0 # 禁用 HTTP/2 服务器
升级检查清单
升级前
- 检查当前 Go 版本:
go version - 备份项目代码
- 检查是否使用了 nil 指针相关的危险模式
- 审查错误处理顺序
- 记录当前性能基准
升级步骤
bash
# 1. 下载 Go 1.25.5
# 从 https://go.dev/dl/ 下载对应平台版本
# 2. 安装
# macOS/Linux:
sudo tar -C /usr/local -xzf go1.25.5.linux-amd64.tar.gz
# 3. 验证安装
go version
# 应输出: go version go1.25.5 ...
# 4. 更新 go.mod
go mod edit -go=1.25
# 5. 下载依赖
go mod download
# 6. 重新构建
go build ./...
# 7. 运行测试
go test ./...
升级后验证
- 所有测试通过
- 应用正常启动
- 检查容器环境中的 GOMAXPROCS 值
- 监控性能指标
- 检查日志中的新警告
- 验证调试信息正常
性能测试对比
go
// benchmark_test.go
package main
import (
"runtime"
"testing"
)
// 测试 GOMAXPROCS 行为
func BenchmarkGOMAXPROCS(b *testing.B) {
b.Logf("GOMAXPROCS: %d", runtime.GOMAXPROCS(0))
b.Logf("NumCPU: %d", runtime.NumCPU())
for i := 0; i < b.N; i++ {
// 你的工作负载
_ = runtime.GOMAXPROCS(0)
}
}
// 测试切片分配
func BenchmarkSliceAlloc(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
// Go 1.25 优化了栈分配
data := make([]byte, 1024)
_ = data
}
}
运行对比:
bash
# Go 1.24
go test -bench=. -benchmem
# Go 1.25
go test -bench=. -benchmem
# 对比结果,特别关注:
# - allocs/op (分配次数应该减少)
# - ns/op (时间应该减少)
容器化最佳实践
Dockerfile 示例
dockerfile
# 使用 Go 1.25.5
FROM golang:1.25.5-alpine AS builder
WORKDIR /app
# 复制依赖文件
COPY go.mod go.sum ./
RUN go mod download
# 复制源代码
COPY . .
# 构建应用
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
# 运行阶段
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/myapp .
# Go 1.25 会自动识别容器 CPU 限制
# 无需手动设置 GOMAXPROCS!
EXPOSE 8080
CMD ["./myapp"]
Kubernetes 部署
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
template:
spec:
containers:
- name: myapp
image: myapp:go1.25.5
resources:
requests:
cpu: "1"
memory: "512Mi"
limits:
cpu: "4" # Go 1.25 会自动使用这个限制!
memory: "2Gi"
# 无需设置 GOMAXPROCS 环境变量
常见问题与解答
Q1: 我应该立即升级到 Go 1.25.5 吗?
A: 推荐升级,原因:
- 包含重要安全修复 (CVE-2025-61729)
- 容器环境性能改进
- 修复了 nil 指针检查 bug
- ⚠️ 建议先在测试环境验证
Q2: 实验性功能可以在生产环境使用吗?
A: 不推荐:
- Green Tea GC - 仍在实验阶段
- JSON v2 - API 可能变化
- 可在测试环境试用并反馈
Q3: 容器感知 GOMAXPROCS 会影响现有应用吗?
A: 大多数情况改进性能:
- 减少不必要的上下文切换
- 更好的资源利用
- ⚠️ 如果手动设置过 GOMAXPROCS,新特性会被禁用
- 可用
GODEBUG禁用此特性
Q4: 如何检查我的代码是否受 nil 指针 bug 影响?
bash
# 1. 搜索危险模式
grep -rn "err :=" . | grep -A 5 "if err"
# 2. 使用 vet 工具
go vet ./...
# 3. 代码审查重点:
# - 是否在检查 err 之前使用了返回值?
# - 特别注意 os.Open, os.Create 等函数
Q5: macOS 11 用户怎么办?
A: 需要升级或使用旧版本:
- 升级到 macOS 12 或更高 (推荐)
- 继续使用 Go 1.24 (有安全风险)
- 使用 Docker 容器开发
迁移建议
低风险项目 (推荐直接升级)
graph LR
A[简单 Web 服务] --> E[ 直接升级]
B[CLI 工具] --> E
C[微服务] --> E
D[批处理脚本] --> E
E --> F[测试]
F --> G[部署]
- Web API 服务
- 命令行工具
- 不涉及复杂并发的应用
- 不依赖实验性功能的项目
中等风险项目 (先测试再升级)
graph TD
A[复杂应用] --> B[创建测试分支]
B --> C[升级 Go 版本]
C --> D[运行完整测试套件]
D --> E{测试通过?}
E -->|是| F[性能测试]
E -->|否| G[修复问题]
G --> D
F --> H{性能符合预期?}
H -->|是| I[逐步推广]
H -->|否| J[分析性能问题]
J --> G
- 高并发系统
- 关键业务应用
- 使用大量 goroutines
- 有严格性能要求
高风险项目 (谨慎评估)
sequenceDiagram
participant Team as 团队
participant Test as 测试环境
participant Stage as 预发布环境
participant Prod as 生产环境
Team->>Test: 1. 部署 Go 1.25.5
Test->>Test: 2. 功能测试 (1周)
Test->>Test: 3. 压力测试
Test->>Team: 4. 收集问题
Team->>Stage: 5. 修复后部署
Stage->>Stage: 6. 灰度验证 (1-2周)
Stage->>Team: 7. 监控指标
Team->>Prod: 8. 逐步升级
Prod->>Team: 9. 持续监控
- 金融交易系统
- 实时数据处理
- 有状态服务
- 零停机时间要求
资源链接
官方文档
社区资源
工具推荐
bash
# 版本管理
go install golang.org/dl/go1.25.5@latest
go1.25.5 download
# 依赖更新检查
go install github.com/oligot/go-mod-upgrade@latest
go-mod-upgrade
# 安全扫描
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...
# 性能分析
go test -cpuprofile=cpu.prof -memprofile=mem.prof -bench=.
go tool pprof cpu.prof
最佳实践总结
DO
- 及时升级到 1.25.5 - 包含重要安全修复
- 充分测试 - 特别是错误处理逻辑
- 利用新特性 - 如 WaitGroup.Go(), testing/synctest
- 监控容器资源 - 验证 GOMAXPROCS 行为
- 更新 go.mod - 声明 Go 1.25 获得新特性
- 阅读迁移指南 - 了解可能的破坏性变更
DON'T
- 不要在生产环境使用实验性功能 - 除非充分测试
- 不要忽略 vet 警告 - 可能指示真实问题
- 不要手动设置 GOMAXPROCS - 除非有特殊需求
- 不要跳过测试阶段 - 直接部署到生产
- 不要混用旧的错误处理模式 - 统一代码风格
- 不要假设向后兼容 - 验证关键路径
版本对比快查表
| 特性 | Go 1.24 | Go 1.25.5 | 备注 |
|---|---|---|---|
| 容器感知 GOMAXPROCS | ❌ | ✅ | 需 go.mod 1.25 |
| testing/synctest | 🧪 实验 | ✅ 正式 | 并发测试利器 |
| JSON v2 | ❌ | 🧪 实验 | 需 GOEXPERIMENT |
| Green Tea GC | ❌ | 🧪 实验 | 需 GOEXPERIMENT |
| DWARF 5 | ❌ | ✅ 默认 | 可禁用 |
| nil 指针检查 | 🐛 Bug | ✅ 修复 | 破坏性修复 |
| os.Root 符号链接 | ❌ | ✅ | 新增 API |
| WaitGroup.Go | ❌ | ✅ | 简化并发 |
| 最低 macOS 版本 | 11 | 12 | ⚠️ 不兼容 |
筒子们这点非常重要 - 请务必看完这句话
Go 1.25.5 已发布,包含重要安全修复和性能改进。建议评估后升级,优先在测试环境验证,特别关注容器部署和错误处理逻辑。对于生产环境,可选择灰度发布或逐步升级策略。实验性功能(Green Tea GC, JSON v2)不建议在生产环境使用,但可在测试环境试用并向社区反馈。
本文基于 Go 1.25.5 官方文档整理,最后更新: 2025年12月24日
感谢整个 Go 社区的贡献!
