# Is there a Hash-like data structure that's not referencing its own elements?

**URL:** <https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556>\
**Category:** Help & Support\
**Created:** [April 12, 2023, 5:51pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556 "2023-04-12T17:51:33Z")\
**Posts on this page:** 6\
**Page:** 2

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [May 1, 2023, 11:45am UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/23 "2023-05-01T11:45:06Z")

</div>

Thanks, the “one off” shouldn’t really hurt, you’re right.  
But it doesn’t seem to work for me (Crystal 1.8.1 on Ubuntu)…

```crystal
class X
    def initialize
        GC.before_collect do
        end
    end
end

X.new
GC.collect

```

… leads to…

```plaintext
Stack overflow (e.g., infinite or very deep recursion)
[0x55c2e81c47c6] *Exception::CallStack::print_backtrace:Nil +118 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b3ff6] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +310 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x7fdd94d39980] ?? +140589661395328 in /lib/x86_64-linux-gnu/libpthread.so.0
[0x55c2e81b13c1] ~procProc(Nil) +17 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b14c7] ~procProc(Nil) +279 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp (140587164552344 times)
[0x55c2e823dd10] GC_mark_some +1392 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e823693d] ?? +94295606651197 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8236719] GC_try_to_collect_inner +329 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e82374a8] ?? +94295606654120 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e823751a] GC_gcollect +10 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81d9346] *GC::collect:Nil +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81a3d2b] __crystal_main +1051 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8235c96] *Crystal::main_user_code<Int32, Pointer(Pointer(UInt8))>:Nil +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e8235c0a] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +58 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x55c2e81b12d6] main +6 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp
[0x7fdd940e2c87] __libc_start_main +231 in /lib/x86_64-linux-gnu/libc.so.6
[0x55c2e81a382a] _start +42 in .../snap/crystal/common/.cache/crystal/crystal-run-gc.tmp

```

---

<div class="post-metadata">

**Author:** ![bcardiff](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/bcardiff/32/3_2.png) [@bcardiff](https://forum.crystal-lang.org/u/bcardiff)\
**Post date:** [May 1, 2023, 1:45pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/24 "2023-05-01T13:45:58Z")

</div>

Hm… right, sorry. `GC.before_collect` might not be ready for this. It seems it does not accept an arbitrary number of calls, only the one that is currently in place. Procs passed end up calling itself because of limitation on how before\_collect is currently written to avoid generating closures.

If we need to hook on before collect we need to offer a better API for it.

---

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [September 28, 2023, 1:30pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/25 "2023-09-28T13:30:11Z")

</div>

it just occurred to me that this is also the proper general (long-living) _caching_ data structure (vs. `Hash`). 😃  
Because the `GC` cleans the cache if the objects are no longer existing.  
At least for class instance related data…

We should just remove any enumeration-/indexing-like methods from this class (can lead to non-deterministic behavior), to have it just serve as a mapper.

---

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [May 8, 2024, 5:30pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/26 "2024-05-08T17:30:09Z")

</div>

I just recently realised that the cleaning approach above with a Fiber per instance is a bad thing. If your applications runs for a while you end up with 32768 fibers and then chokes…

---

<div class="post-metadata">

**Author:** ![jgaskins](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/jgaskins/32/2449_2.png) [@jgaskins](https://forum.crystal-lang.org/u/jgaskins)\
**Post date:** [May 8, 2024, 9:19pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/27 "2024-05-08T21:19:48Z")

</div>

> [@wolfgang371](#):
>
> you end up with 32768 fibers and then chokes

Is 32767 the max number of fibers a program can have? Is the scheduler limited to an `Int16` range of fibers?

---

<div class="post-metadata">

**Author:** ![wolfgang371](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/wolfgang371/32/1665_2.png) [@wolfgang371](https://forum.crystal-lang.org/u/wolfgang371)\
**Post date:** [May 9, 2024, 12:08pm UTC](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556/28 "2024-05-09T12:08:22Z")

</div>

I’m not sure, where it really comes from; the console tells something about out-of-memory, but that’s not true.  
For this scenario I was actually lucky that the error showed up as early as ~32k fibers; otherwise I would have realized much later that the number of fibers in my program is/was unbounded.

```crystal
count = 0
40000.times do |i|
    p i
    spawn do
        while(true)
        end
    end
end

# [...]
# 32734
# 32735
# 32736
# 32737
# 32738
# 32739
# Unhandled exception: Cannot allocate new fiber stack: Cannot allocate memory (RuntimeError)
# GC Warning: Out of memory - trying to allocate requested amount (512 bytes)...
# GC Warning: Out of memory - trying to allocate requested amount (336 bytes)...
# GC Warning: Out of memory - trying to allocate requested amount (336 bytes)...
# GC Warning: Header allocation failed: dropping block
# GC Warning: Failed to expand heap by 2031616 bytes
# GC Warning: Failed to expand heap by 262144 bytes
# GC Warning: Out of Memory! Heap size: 5 MiB. Returning NULL!
# Invalid memory access (signal 11) at address 0xc0
# [0x559772385726] *Exception::CallStack::print_backtrace:Nil +118 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x5597723741f6] ~procProc(Int32, Pointer(LibC::SiginfoT), Pointer(Void), Nil) +310 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x7f1fe2f42980] ?? +139774928365952 in /lib/x86_64-linux-gnu/libpthread.so.0
# [0x559772410f2a] *Pointer(Crystal::DWARF::LineNumbers::Row) +186 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240ffba] *Array(Crystal::DWARF::LineNumbers::Row) +218 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240fed1] *Array(Crystal::DWARF::LineNumbers::Row) +145 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977240bea4] *Crystal::DWARF::LineNumbers#register_to_matrix<Crystal::DWARF::LineNumbers::Sequence, Crystal::DWARF::LineNumbers::Register>:Nil +1876 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772409d63] *Crystal::DWARF::LineNumbers#read_statement_program<Crystal::DWARF::LineNumbers::Sequence>:Nil +3187 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772407534] *Crystal::DWARF::LineNumbers#decode_sequences<(UInt32 | UInt64)>:Nil +6644 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772405b20] *Crystal::DWARF::LineNumbers#initialize<IO::FileDescriptor+, (UInt32 | UInt64), UInt64, (Crystal::DWARF::Strings | Nil), (Crystal::DWARF::Strings | Nil)>:Nil +592 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x5597724057bc] *Crystal::DWARF::LineNumbers::new<IO::FileDescriptor+, (UInt32 | UInt64), UInt64, (Crystal::DWARF::Strings | Nil), (Crystal::DWARF::Strings | Nil)>:Crystal::DWARF::LineNumbers +1020 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237ef02] *Exception::CallStack::read_dwarf_sections<String, UInt64>:(Array(Tuple(UInt64, UInt64, String)) | Nil) +14290 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b725] *Exception::CallStack::load_debug_info_impl:Nil +277 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b51c] *Exception::CallStack::load_debug_info:Nil +156 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237b20b] *Exception::CallStack::decode_line_number<UInt64>:Tuple(String, Int32, Int32) +27 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237aa36] *Exception::CallStack#decode_backtrace:Array(String) +310 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977237a8e2] *Exception::CallStack#printable_backtrace:Array(String) +50 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772419b3b] *Exception+ +75 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772419bd9] *Exception+ +105 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x55977242882a] *Crystal::exit<Int32, (Exception+ | Nil)>:Int32 +90 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772428769] *Crystal::main<Int32, Pointer(Pointer(UInt8))>:Int32 +105 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x559772371d36] main +6 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x7f1fe22ebc87] __libc_start_main +231 in /lib/x86_64-linux-gnu/libc.so.6
# [0x5597723642ba] _start +42 in /home/wolfgang/snap/crystal/common/.cache/crystal/crystal-run-fibers.tmp
# [0x0] ???

```

[Previous page](https://forum.crystal-lang.org/t/is-there-a-hash-like-data-structure-thats-not-referencing-its-own-elements/5556.md?page=1)
