Skip to content

Commit 767ccdf

Browse files
mknyszekgopherbot
authored andcommitted
_content/doc/gc-guide: add section on object death features
That is, finalizers, cleanups, and weak pointers. For golang/go#67552. For golang/go#67535. Change-Id: I673a976e3bb94ab0ad282e4e053360292034eae6 Reviewed-on: https://go-review.googlesource.com/c/website/+/637555 Reviewed-by: Carlos Amedee <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Auto-Submit: Michael Knyszek <[email protected]>
1 parent 0c27690 commit 767ccdf

File tree

1 file changed

+178
-2
lines changed

1 file changed

+178
-2
lines changed

_content/doc/gc-guide.html

+178-2
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ <h3 id="Understanding_costs">Understanding costs</h3>
391391
</p>
392392

393393
<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
395395
earlier.
396396
And <b>GC frequency</b> is at the center of this trade-off:
397397
if we execute the GC more frequently, then we use less memory, and vice versa.
@@ -998,6 +998,182 @@ <h3 id="Latency">Latency</h3>
998998
pointer writes requiring additional work.
999999
</p>
10001000

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&mdash;those reachable from a global variable or a
1016+
computation in some goroutine&mdash;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+
10011177
<!-- TODO: Add a short section about non-steady-state behavior. -->
10021178

10031179
<h3 id="Additional_resources">Additional resources</h3>
@@ -1238,7 +1414,7 @@ <h3 id="Identiying_costs">Identifying costs</h3>
12381414
</p>
12391415

12401416
<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
12421418
<code>runtime/trace</code></a> package for how to get started with
12431419
execution traces.
12441420
</p>

0 commit comments

Comments
 (0)