@@ -391,7 +391,7 @@ <h3 id="Understanding_costs">Understanding costs</h3>
391
391
</ p >
392
392
393
393
< p >
394
- This change in overheads is the fundamental time/space trade-off mentioned
394
+ This change in overheads is the fundamental time/space trade-off mentioned
395
395
earlier.
396
396
And < b > GC frequency</ b > is at the center of this trade-off:
397
397
if we execute the GC more frequently, then we use less memory, and vice versa.
@@ -998,6 +998,182 @@ <h3 id="Latency">Latency</h3>
998
998
pointer writes requiring additional work.
999
999
</ p >
1000
1000
1001
+ < h3 id ="Finalizers_cleanups_and_weak_pointers "> Finalizers, cleanups, and weak pointers</ h2 >
1002
+
1003
+ < p >
1004
+ Garbage collection provides the illusion of infinite memory using only finite
1005
+ memory.
1006
+ Memory is allocated but never explicitly freed, which enables simpler APIs and
1007
+ concurrent algorithms compared to bare-bones manual memory management.
1008
+ (Some languages with manually managed memory use alternative approaches such
1009
+ as "smart pointers" and compile-time ownership tracking to ensure that objects
1010
+ are freed, but these features are deeply embedded into the API design
1011
+ conventions in these languages.)
1012
+ </ p >
1013
+
1014
+ < p >
1015
+ Only the live objects—those reachable from a global variable or a
1016
+ computation in some goroutine—can affect the behavior of the program.
1017
+ Any time after an object becomes unreachable ("dead"), it may be safely
1018
+ recycled by the GC.
1019
+ This allows for a wide variety of GC designs, such as the tracing design used
1020
+ by Go today.
1021
+ The death of an object is not an observable event at the language level.
1022
+ </ p >
1023
+
1024
+ < p >
1025
+ However, Go's runtime library provides three features that break that illusion:
1026
+ < a href ="/pkg/runtime#SetFinalizer "> finalizers</ a > ,
1027
+ < a href ="/pkg/runtime#AddCleanup "> cleanups</ a > ,
1028
+ and < a href ="/pkg/weak#Pointer "> weak pointers</ a > .
1029
+ Each of these features provides some way to observe and react to object death,
1030
+ and in the case of finalizers, even reverse it.
1031
+ This of course complicates Go programs and adds an additional burden to the GC
1032
+ implementation.
1033
+ Nonetheless, these features exist because they are useful in a variety of
1034
+ circumstances, and Go programs use them and benefit from them all the time.
1035
+ </ p >
1036
+
1037
+ < h4 id ="Advice "> Advice</ h4 >
1038
+
1039
+ < p >
1040
+ For the details of each feature, refer to its package documentation
1041
+ (< a href ="/pkg/runtime#SetFinalizer "> runtime.SetFinalizer</ a > ,
1042
+ < a href ="/pkg/runtime#AddCleanup "> runtime.AddCleanup</ a > ,
1043
+ < a href ="/pkg/weak#Pointer "> weak.Pointer</ a > ).
1044
+ Below is some general advice for using these features.
1045
+ </ p >
1046
+
1047
+ < ul >
1048
+ < li >
1049
+ < p >
1050
+ < b >
1051
+ Avoid using these features directly in typical Go code.
1052
+ </ b >
1053
+ </ p >
1054
+ < p >
1055
+ These are low-level features with subtle restrictions and behaviors.
1056
+ For instance, there's no guarantee cleanups or finalizers will be run
1057
+ at program exit, or at all for that matter.
1058
+ The long comments in their API documentation should be seen as a warning.
1059
+ The vast majority of Go code does not benefit from using these features
1060
+ directly, only indirectly.
1061
+ </ p >
1062
+ </ li >
1063
+ < li >
1064
+ < p >
1065
+ < b >
1066
+ Encapsulate the use of these mechanisms within a package.
1067
+ </ b >
1068
+ </ p >
1069
+ < p >
1070
+ Where possible, do not allow the use of these mechanisms to leak into
1071
+ the public API of your package; provide interfaces that make it hard or
1072
+ impossible for users to misuse them.
1073
+ For example, instead of asking the user to set up a cleanup on some
1074
+ C-allocated memory to free it, write a wrapper package and hide that
1075
+ detail inside.
1076
+ </ p >
1077
+ </ li >
1078
+ < li >
1079
+ < p >
1080
+ < b >
1081
+ Restrict access to objects that have finalizers, cleanups, and weak
1082
+ pointers to the package that created and applied them.
1083
+ </ b >
1084
+ </ p >
1085
+ < p >
1086
+ This is related to the previous point, but is worth calling out
1087
+ explicitly, since it's a very powerful pattern for using these
1088
+ features in a less error-prone way.
1089
+ For example, the < a href ="/pkg/unique "> unique package</ a > uses
1090
+ weak pointers under the hood, but completely encasulates the objects
1091
+ that are weakly pointed-to.
1092
+ Those values can never be mutated by the rest of the application,
1093
+ it can only be copied through the
1094
+ < a href ="/pkg/unique#Handle.Value "> Value method</ a > , preserving
1095
+ the illusion of infinite memory for package users.
1096
+ </ p >
1097
+ </ li >
1098
+ < li >
1099
+ < p >
1100
+ < b >
1101
+ Prefer cleaning up non-memory resources deterministically when possible,
1102
+ with finalizers and cleanups as a fallback.
1103
+ </ b >
1104
+ </ p >
1105
+ < p >
1106
+ Cleanups and finalizers are a good fit for memory resources such as
1107
+ memory allocated externally, like from C, or references to an
1108
+ < code > mmap</ code > mapping.
1109
+ Memory allocated by C's malloc must eventually be freed by C's free.
1110
+ A finalizer that calls < code > free</ code > , attached to a wrapper object
1111
+ for the C memory, is a reasonable way to ensure that C memory is
1112
+ eventually reclaimed as a consequence of garbage collection.
1113
+ </ p >
1114
+ < p >
1115
+ However, non-memory resources, like file descriptors, tend to be subject to
1116
+ system limits that the Go runtime is generally unaware of.
1117
+ In addition, the timing of the garbage collector in a given Go program
1118
+ is usually something a package author has little control over (for instance,
1119
+ how often the GC runs is controlled by < a href ="#GOGC "> GOGC</ a > , which can
1120
+ be set by operators to a variety of different values in practice).
1121
+ These two facts conspire to make cleanups and finalizers a bad fit to use
1122
+ as the only mechanism for releasing non-memory resources.
1123
+ </ p >
1124
+ < p >
1125
+ If you're a package author exposing an API that wraps some non-memory
1126
+ resource, consider providing an explicit API for releasing the resource
1127
+ deterministically (through a < code > Close</ code > method, or something similar),
1128
+ rather than relying on the garbage collector through cleanups or finalizers.
1129
+ Instead, prefer to use cleanups and finalizers as a best-effort handler for
1130
+ programmer mistakes, either by cleaning up the resource anyway like
1131
+ < a href ="/pkg/os#File "> os.File</ a > does, or by reporting the failure to
1132
+ deterministically clean up back to the user.
1133
+ </ p >
1134
+ </ li >
1135
+ < li >
1136
+ < p >
1137
+ < b >
1138
+ Prefer cleanups to finalizers.
1139
+ </ b >
1140
+ </ p >
1141
+ < p >
1142
+ Historically, finalizers were added to simplify the interface between Go code
1143
+ and C code and to clean up non-memory resources.
1144
+ The intended use was to apply them to wrapper objects that owned C memory or
1145
+ some other non-memory resource, so that the resource could be released once
1146
+ Go code was done using it.
1147
+ These reasons at least partially explain why finalizers are narrowly scoped,
1148
+ why any given object can only have one finalizer, and why that finalizer must
1149
+ be attached to the first byte of the object only.
1150
+ This limitation already stifles some use-cases.
1151
+ For example, any package that wishes to internally cache some information about
1152
+ an object passed to it cannot clean up that information once the object is gone.
1153
+ </ p >
1154
+ < p >
1155
+ But worse than that, finalizers are inefficient and error-prone due to the fact
1156
+ that they < a href ="https://en.wikipedia.org/wiki/Object_resurrection "> resurrect
1157
+ the object</ a > they're attached to, so that it can be passed to the finalizer
1158
+ function (and even continue to live beyond that, too).
1159
+ This simple fact means that if the object is part of a reference cycle it can
1160
+ never be freed, and the memory backing the object cannot be reused until at
1161
+ least the following garbage collection cycle.
1162
+ </ p >
1163
+ < p >
1164
+ Because finalizers resurrect objects, though, they do have a better-defined
1165
+ exection order than cleanups.
1166
+ For this reason, finalizers are still potentially (but rarely) useful for
1167
+ cleaning up structures that have complex destruction ordering requirements.
1168
+ </ p >
1169
+ < p >
1170
+ But for all other uses in Go 1.24 and beyond, we recommend you use cleanups
1171
+ because they are more flexible, less error-prone, and more efficient than
1172
+ finalizers.
1173
+ </ p >
1174
+ </ li >
1175
+ </ ul >
1176
+
1001
1177
<!-- TODO: Add a short section about non-steady-state behavior. -->
1002
1178
1003
1179
< h3 id ="Additional_resources "> Additional resources</ h3 >
@@ -1238,7 +1414,7 @@ <h3 id="Identiying_costs">Identifying costs</h3>
1238
1414
</ p >
1239
1415
1240
1416
< p >
1241
- See the < a href ="https://pkg.go.dev/runtime/trace "> documentation for the
1417
+ See the < a href ="https://pkg.go.dev/runtime/trace "> documentation for the
1242
1418
< code > runtime/trace</ code > </ a > package for how to get started with
1243
1419
execution traces.
1244
1420
</ p >
0 commit comments