Skip to content

Instantly share code, notes, and snippets.

@dralletje
Created November 30, 2020 02:09
Show Gist options
  • Save dralletje/cab0ef927e8928150583e2ad631fd082 to your computer and use it in GitHub Desktop.
Save dralletje/cab0ef927e8928150583e2ad631fd082 to your computer and use it in GitHub Desktop.
### A Pluto.jl notebook ###
# v0.12.15
using Markdown
using InteractiveUtils
# ╔═╡ d07a2949-f621-4714-9edf-33660d83ee0a
md"""
# Experiments with Garbage Collection
I create a variable, either an array wrapped in an MemoryHog (for clarity) or a module. Then I do some things with it (define it in local scope, global scope, putting it inside a module) and then the status message at the top tells me if **after this cell is done running**, the variable is GC'ed or not.
"""
# ╔═╡ 6703fcad-3748-4c8a-9dc0-d0b8a65d5ad1
md"### Local definition"
# ╔═╡ a17357a7-eee7-4f94-aa05-93f0a3449642
md"#### Simple, just defined and track it"
# ╔═╡ 6680f2a2-27a0-418c-b892-f3c75da6aed9
md"#### Also define a function that uses it, and expose that"
# ╔═╡ ddf6ebb7-0e4a-4f3c-80e7-e5d957811873
md"#### Same function, just local this time"
# ╔═╡ 8f180aeb-80d5-49a5-9533-45e49daba447
md"""
### Global definition
Looking at the current allocation status doesn't make sense here: we put the variable in the global scope, so obviously it needs to be available for other cells still.
Re-run the `global_allocation` cell repeatedly to see the status of
previous values update. There should be "1 out of X not finalized", which is the current one.
"""
# ╔═╡ 32e6e8d0-be99-4f9b-9102-be6f43f53c1c
tracked_memory_hog = []
# ╔═╡ b6a7aad1-72e6-4e40-a0d0-94d3a6ab9093
md"#### GC'ing a module"
# ╔═╡ 4d43b24c-e63f-4500-abea-d7d247be1a63
md"#### GC'ing a module with struct or function"
# ╔═╡ 2335237e-103c-4e30-aae1-42bc65e016bf
md"#### Injected into a new, local, module"
# ╔═╡ bedb3e48-f827-47a2-87be-445ba0543efa
md"#### GC'ing a 'real' module"
# ╔═╡ aaca5e61-ccd1-4653-ad07-9db5762d787d
md"#### Inside module with function inside"
# ╔═╡ 84a0fdb2-6523-4cc8-be9b-334beb3ddfca
md"""
#### Imported into module, deleted in original module
(This is what Pluto does)
"""
# ╔═╡ 022f2b42-bcd7-4cb5-9f88-08be483dfd50
md"#### Inside a module with a const"
# ╔═╡ 4eb741b5-0d10-4385-bf31-32909b2f263a
md"#### Defined in new module, but also deleted"
# ╔═╡ c6e8c5e1-f441-4569-aab3-d958389f1d19
md"#### Defined in a module that gets 'replaced'"
# ╔═╡ 6c10d6ec-2554-4d5f-9319-ff678c4acdec
md"""
#### Weird contraption with extension to Base
Don't do this.
(change `include_memory_hog_in_result` for Julia magic)
"""
# ╔═╡ e61ac897-1872-4a05-8ef3-f30f95db9178
struct UselessStruct end
# ╔═╡ 181ebdb1-3789-4bf5-b67a-e73298d55fa7
include_memory_hog_in_result = false
# ╔═╡ d6e32657-3ed9-4845-bda8-0ee58bb4dcf0
base_extension_trackers = []
# ╔═╡ 294467a8-20dc-4dcd-8f93-89b3dc548ce9
md"## Appendix"
# ╔═╡ 442c27aa-9a34-4b33-b228-d473823e0e76
md"**Bytes**"
# ╔═╡ 47b9494a-1d84-4b7e-adb1-40e2e04a79e2
struct Bytes
value
end
# ╔═╡ 491310d3-dfe3-41a8-927c-446886c6913c
function Base.show(io::IO, ::MIME"text/plain", bytes::Bytes)
amount, unit = if bytes.value > 1e9
(bytes.value / 1e9, "gb")
elseif bytes.value > 1e6
(bytes.value / 1e6, "mb")
elseif bytes.value > 1e3
(bytes.value / 1e3, "kb")
else
(bytes.value, "b")
end
print(io, "$(round(amount, digits=2))$(unit)")
end
# ╔═╡ 878aa1d1-39d2-434d-8af8-08ed2e0c6d4b
md"**Finalization helper**"
# ╔═╡ 5f26becf-b856-49c5-ae31-bf48fe76ee72
Base.@kwdef mutable struct FinalizeTracker
is_finalized::Bool = false
end
# ╔═╡ f23464a2-9844-448b-a60e-dbc542ddc2a3
function track_finalize(@nospecialize(object))
tracker = FinalizeTracker()
# ref = Ref(false)
finalizer(object) do x
tracker.is_finalized = true
# ref[] = true
end
tracker
# ref
end
# ╔═╡ 532b773e-2bee-4899-a6b6-03296422b71d
module_definition_plus = begin
local module_definition_module = Module(:MemoryHogModule)
Core.eval(module_definition_module, quote function X() nothing end end)
# Core.eval(module_definition_module, quote struct X nothing end end)
track_finalize(module_definition_module)
end
# ╔═╡ ad41c1c9-436e-4c35-ae11-363f081a3ae2
module_definition_real = begin
local module_definition_module = Core.eval(Main, :(module MemoryHogModule end))
# Try replacing it even
Core.eval(Main, :(module MemoryHogModule end))
track_finalize(module_definition_module)
end
# ╔═╡ c4af3d7c-920e-4e86-b58e-50de840fa108
function status_message(color, message)
HTML("""
<div style="
display: flex;
flex-direction: row;
align-items: center;
padding: 4px;
">
<div style="
width: 16px;
height: 16px;
border-radius: 8px;
background-color: $(color);
"></div>
<div style="width: 8px"></div>
<div>$(message)</div>
</div>
""")
end
# ╔═╡ f1b31e9f-7f8a-4ac9-8cb3-88fe1e25cd46
function force_gc(tracker::FinalizeTracker)
if tracker.is_finalized
status_message("black", "Already finalized?!")
else
GC.gc(false)
if tracker.is_finalized
status_message("green", "Finalized after shallow GC")
else
GC.gc(true)
if tracker.is_finalized
status_message("lightgreen", "Finalized after deep GC")
else
status_message("red", "Not finalized after GC")
end
end
end
end
# ╔═╡ 153bd296-6b7f-49e7-9911-8311e33aa793
force_gc(module_definition_plus)
# ╔═╡ 42247aa2-29d8-49df-81fc-a22230b0f170
force_gc(module_definition_real)
# ╔═╡ 58cc3ce8-1fb2-4bda-8d83-5cb32dafad8d
Base.@kwdef mutable struct MemoryHog
value = zeros(10, 10, 10)
end
# ╔═╡ 21d5544d-ed06-4bfc-87c7-0a64e9a77802
local_allocation = begin
local memory_hog = MemoryHog()
track_finalize(memory_hog)
end
# ╔═╡ 7c8d062b-54b5-46a8-9a67-7e8c0f3070bc
force_gc(local_allocation)
# ╔═╡ 4fc8797c-3d15-4a89-989f-4bc0d2107b05
local_allocation_with_closure = begin
local memory_hog = MemoryHog()
fn = () -> memory_hog
track_finalize(memory_hog)
end
# ╔═╡ fb7aac8d-703d-4b30-8c36-bfd019966efe
force_gc(local_allocation_with_closure)
# ╔═╡ 72ba1f46-0e0d-4853-9dd9-d9306a84b079
local_allocation_with_local_closure = begin
local memory_hog = MemoryHog()
track_finalize(memory_hog)
end
# ╔═╡ 925d61f4-3087-4dc0-a4a3-ddfd8ee6ce6e
force_gc(local_allocation_with_local_closure)
# ╔═╡ d36cc63b-25ac-47e4-b1d7-64961e43a89b
global_allocation = begin
# @__MODULE__ # Uncomment this to try the non-fast-tracked version
memory_hog = MemoryHog()
local tracking = track_finalize(memory_hog)
# Even though functions can't be finalized,
# they sure are being dereferences (and thus memory_hog)
fn2 = () -> memory_hog
# push!(cached_modules, @__MODULE__)
push!(tracked_memory_hog, tracking)
tracking
end
# ╔═╡ 26433879-4009-41b7-a04c-a748b04f8752
global_allocation; begin
force_gc(global_allocation);
local finalized = length(filter(tracked_memory_hog) do tracker
tracker.is_finalized
end)
local not_finalized = length(filter(tracked_memory_hog) do tracker
!tracker.is_finalized
end)
Text("$(not_finalized) out of $(not_finalized + finalized) not finalized")
end
# ╔═╡ 2942b097-9962-4840-9570-438d15b9775b
module_definition = begin
local memory_hog = MemoryHog(Module(:MemoryHogModule))
track_finalize(memory_hog)
end
# ╔═╡ 788e9fcc-ed6e-4bbc-a706-6500df81ff7f
force_gc(module_definition)
# ╔═╡ d3f38b6b-5614-46fe-93ca-c084c389469d
new_module_definition = begin
local memory_hog = MemoryHog()
local m = Module(:MemoryHogModule)
Core.eval(m, quote inner_memory_hog = $(memory_hog) end)
track_finalize(memory_hog)
end
# ╔═╡ d49e7cc7-3903-4bef-b457-1e2ca1543642
force_gc(new_module_definition)
# ╔═╡ 60042f23-9c6d-490a-8cda-6c6ffbf3a6e8
module_with_function_definition = begin
local memory_hog = MemoryHog()
local m = Module(:NewModuleDefinition)
Core.eval(m, quote inner_memory_hog = $(memory_hog) end)
Core.eval(m, quote function() nothing end end)
track_finalize(memory_hog)
end
# ╔═╡ 3539a896-20c8-4d86-98d9-872ee63f41a1
force_gc(module_with_function_definition)
# ╔═╡ 9c557295-cfc8-47d1-a88d-ecf42a774046
module_import_module = begin
local memory_hog = MemoryHog()
local First = Core.eval(Main, :(
module First
inner_memory_hog = $(memory_hog)
end
))
local Second = Core.eval(Main, :(
module Second
First = $(First)
import .First: inner_memory_hog
end
))
Core.eval(First, quote
inner_memory_hog = nothing
end)
track_finalize(memory_hog)
end
# ╔═╡ cede163c-7a76-4d9b-95ee-fae936550f39
force_gc(module_import_module)
# ╔═╡ 433321fe-69d1-4c89-ad3f-61f53b01720e
module_with_const_definition = begin
local memory_hog = MemoryHog()
local m = Module(:NewModuleDefinition)
Core.eval(m, quote inner_memory_hog = $(memory_hog) end)
Core.eval(m, quote const X = Dict() end)
track_finalize(memory_hog)
end
# ╔═╡ 6651459d-959a-49ea-991f-f3bb8cf32d38
force_gc(module_with_const_definition)
# ╔═╡ 1f2cce18-66fb-4e31-a846-90fdb11f15b1
new_module_and_removed_definition = begin
local memory_hog = MemoryHog()
local m = Module(:NewModuleDefinition)
Core.eval(m, quote inner_memory_hog = $(memory_hog) end)
Core.eval(m, quote function() nothing end end)
Core.eval(m, quote inner_memory_hog = nothing end)
track_finalize(memory_hog)
end
# ╔═╡ 9ee2e44c-39b0-4c80-ad95-e8e14a93d775
force_gc(new_module_and_removed_definition)
# ╔═╡ b1aa2274-258c-417d-84c7-851330691db5
real_module_creation = begin
local memory_hog = MemoryHog()
local m1 = Core.eval(Main, :(module MemoryHogModule inner_memory_hog = $(memory_hog) end))
local m1 = Core.eval(Main, :(module MemoryHogModule end))
track_finalize(memory_hog)
end
# ╔═╡ cf2c91c7-ab09-4f38-9b23-ffe8bdd2d55d
force_gc(real_module_creation)
# ╔═╡ 21908ca8-ff1b-48f4-8ffc-fdd22d19a765
reference_in_base_extension = begin
local memory_hog = MemoryHog(Module(:ModuleDefinition))
if include_memory_hog_in_result
function Base.:+(::UselessStruct, ::UselessStruct)
"We are useless"
memory_hog
end
else
# It's still in the function, but julia sees it is being useless
function Base.:+(::UselessStruct, ::UselessStruct)
memory_hog
"We are useless"
end
end
push!(base_extension_trackers, track_finalize(memory_hog))
end
# ╔═╡ 284c94a5-b1a7-416d-9e03-91f165874b6a
reference_in_base_extension; begin
GC.gc(true);
local finalized = length(filter(base_extension_trackers) do tracker
tracker.is_finalized
end)
local not_finalized = length(filter(base_extension_trackers) do tracker
!tracker.is_finalized
end)
Text("$(not_finalized) out of $(not_finalized + finalized) not finalized")
end
# ╔═╡ 5c8c0690-5b1b-485b-9abe-7a9daa2f2e98
status_message("red", "Just an example")
# ╔═╡ 203c2942-f626-405b-a492-0aa9e8623ae6
status_message("green", "Whatcha looking at?")
# ╔═╡ Cell order:
# ╟─d07a2949-f621-4714-9edf-33660d83ee0a
# ╟─6703fcad-3748-4c8a-9dc0-d0b8a65d5ad1
# ╟─a17357a7-eee7-4f94-aa05-93f0a3449642
# ╟─7c8d062b-54b5-46a8-9a67-7e8c0f3070bc
# ╠═21d5544d-ed06-4bfc-87c7-0a64e9a77802
# ╟─6680f2a2-27a0-418c-b892-f3c75da6aed9
# ╟─fb7aac8d-703d-4b30-8c36-bfd019966efe
# ╠═4fc8797c-3d15-4a89-989f-4bc0d2107b05
# ╟─ddf6ebb7-0e4a-4f3c-80e7-e5d957811873
# ╟─925d61f4-3087-4dc0-a4a3-ddfd8ee6ce6e
# ╠═72ba1f46-0e0d-4853-9dd9-d9306a84b079
# ╟─8f180aeb-80d5-49a5-9533-45e49daba447
# ╟─26433879-4009-41b7-a04c-a748b04f8752
# ╠═d36cc63b-25ac-47e4-b1d7-64961e43a89b
# ╟─32e6e8d0-be99-4f9b-9102-be6f43f53c1c
# ╟─b6a7aad1-72e6-4e40-a0d0-94d3a6ab9093
# ╟─788e9fcc-ed6e-4bbc-a706-6500df81ff7f
# ╠═2942b097-9962-4840-9570-438d15b9775b
# ╟─4d43b24c-e63f-4500-abea-d7d247be1a63
# ╟─153bd296-6b7f-49e7-9911-8311e33aa793
# ╠═532b773e-2bee-4899-a6b6-03296422b71d
# ╟─2335237e-103c-4e30-aae1-42bc65e016bf
# ╟─d49e7cc7-3903-4bef-b457-1e2ca1543642
# ╠═d3f38b6b-5614-46fe-93ca-c084c389469d
# ╟─bedb3e48-f827-47a2-87be-445ba0543efa
# ╟─42247aa2-29d8-49df-81fc-a22230b0f170
# ╠═ad41c1c9-436e-4c35-ae11-363f081a3ae2
# ╟─aaca5e61-ccd1-4653-ad07-9db5762d787d
# ╟─3539a896-20c8-4d86-98d9-872ee63f41a1
# ╠═60042f23-9c6d-490a-8cda-6c6ffbf3a6e8
# ╟─84a0fdb2-6523-4cc8-be9b-334beb3ddfca
# ╟─cede163c-7a76-4d9b-95ee-fae936550f39
# ╠═9c557295-cfc8-47d1-a88d-ecf42a774046
# ╟─022f2b42-bcd7-4cb5-9f88-08be483dfd50
# ╟─6651459d-959a-49ea-991f-f3bb8cf32d38
# ╠═433321fe-69d1-4c89-ad3f-61f53b01720e
# ╟─4eb741b5-0d10-4385-bf31-32909b2f263a
# ╟─9ee2e44c-39b0-4c80-ad95-e8e14a93d775
# ╠═1f2cce18-66fb-4e31-a846-90fdb11f15b1
# ╟─c6e8c5e1-f441-4569-aab3-d958389f1d19
# ╟─cf2c91c7-ab09-4f38-9b23-ffe8bdd2d55d
# ╠═b1aa2274-258c-417d-84c7-851330691db5
# ╟─6c10d6ec-2554-4d5f-9319-ff678c4acdec
# ╠═e61ac897-1872-4a05-8ef3-f30f95db9178
# ╟─284c94a5-b1a7-416d-9e03-91f165874b6a
# ╠═181ebdb1-3789-4bf5-b67a-e73298d55fa7
# ╠═21908ca8-ff1b-48f4-8ffc-fdd22d19a765
# ╟─d6e32657-3ed9-4845-bda8-0ee58bb4dcf0
# ╟─294467a8-20dc-4dcd-8f93-89b3dc548ce9
# ╟─442c27aa-9a34-4b33-b228-d473823e0e76
# ╟─47b9494a-1d84-4b7e-adb1-40e2e04a79e2
# ╟─491310d3-dfe3-41a8-927c-446886c6913c
# ╟─878aa1d1-39d2-434d-8af8-08ed2e0c6d4b
# ╟─5f26becf-b856-49c5-ae31-bf48fe76ee72
# ╟─f23464a2-9844-448b-a60e-dbc542ddc2a3
# ╟─c4af3d7c-920e-4e86-b58e-50de840fa108
# ╟─f1b31e9f-7f8a-4ac9-8cb3-88fe1e25cd46
# ╠═58cc3ce8-1fb2-4bda-8d83-5cb32dafad8d
# ╠═5c8c0690-5b1b-485b-9abe-7a9daa2f2e98
# ╠═203c2942-f626-405b-a492-0aa9e8623ae6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment