Skip to content

Commit

Permalink
Merge pull request #89 from devin7788/master
Browse files Browse the repository at this point in the history
#39: Avoid defer in loops, or your memory might blow up
  • Loading branch information
smallnest authored Apr 24, 2024
2 parents 7e63b04 + fbb19df commit fcb382a
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ Go tips from [Phuong Le](https://twitter.com/func25).
| 36 | | |
| 37 | | |
| 38 | Make your errors clear with fmt.Errorf, don't just leave them bare | smallnest |
| 39 | | |
| 39 | Avoid defer in loops, or your memory might blow up | devin7788 |
| 40 | Handle errors while using defer to prevent silent failures | smallnest |
| 41 | | |
| 42 | | |
Expand Down
39 changes: 39 additions & 0 deletions src/039.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Tip #39 避免在循环中使用defer,否则可能会导致内存溢出

> 原始链接:[Golang Tip #39: Avoid defer in loops, or your memory might blow up.](https://twitter.com/func25/status/1764606374519230540)
在 Go 中使用 defer 时,我们一般希望defer后面的函数能在当前函数返回之前被执行。
![](./images/039/001.png)

然而,像这样在循环中放置 defer 是不建议的:

![](./images/039/002.png)


(为简单起见,让我们不考虑 f.Close 的错误处理)。

以下是需要考虑的两个关键点:

1. 执行时间

所有这些延迟调用都在函数即将返回时执行,而不是在循环的每次迭代之后执行。

如果您的循环是长时间运行的函数的一部分,这意味着在很久之后才会执行任何延迟的任务。

当这些延迟的任务用于释放资源或清理时,这尤其成问题。这样我们不能在完成后立即释放资源,而是等到最后才释放。

2. 内存溢出的可能性

每个 defer 都会在内存中添加一个调用点。

在迭代了数百或上千次的循环中,因为每个调用都会消耗内存, 导致堆栈被这些延迟调用填满。

每个延迟调用的细节需要被存储(例如要调用的函数及其参数),这些细节被分配在函数的堆栈帧中(或者根据编译器的策略分配在堆上)。

有几种策略可以减轻影响。其中一个偷懒的修复它的方法,可以考虑使用匿名函数:



![](./images/039/003.png)

我们也可以将这个功能修改成一个具名函数,尽量不要使用 defer(除非这个函数有panic 的风险)。
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- [Tip #31 使用跳转标签让break和continue语句更简洁](./031.md)
- [Tip #33 尽量...不要使用panic()](./033.md)
- [Tip #38 使用 fmt.Errorf 使你的错误信息清晰明了,不要让它们过于赤裸](./038.md)
- [Tip #39 避免在循环中使用defer,否则可能会导致内存溢出](./039.md)
- [Tip #40 在使用defer时处理错误以防止静默失败](./040.md)
- [Tip #43 优雅关闭你的应用程序](./043.md)
- [Tip #44 有意地使用Must函数来停止程序](./044.md)
Expand Down
Binary file added src/images/039/001.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/039/002.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/images/039/003.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit fcb382a

Please sign in to comment.