JLang icon indicating copy to clipboard operation
JLang copied to clipboard

Some Memory in Runtime Not Recognized by Garbage Collector

Open dz333 opened this issue 4 years ago • 4 comments

While both concurrency and reflection are individually supported, calling any of the reflection methods in a multithreaded program can cause segfaults. This is a high priority issue and was introduced when multithreading support was introduced with: #52

dz333 avatar Dec 17 '19 19:12 dz333

In fact, the segfault has nothing to do with multithreading. It can be reproduced with a single-threaded program (test/isolated/ReflectionMemoryTest.java in #57) After digging into this problem several days, I found that it was due to our garbage collector.

There is a warning in the bdwgc documentation as follows

WARNING: pointers inside memory allocated by the standard malloc are not seen by the garbage collector. Thus objects pointed to only from such a region may be prematurely deallocated. It is thus suggested that the standard malloc be used only for memory regions, such as I/O buffers, that are guaranteed not to contain pointers to garbage collectible memory. Pointers in C language automatic, static, or register variables, are correctly recognized. (Note that GC_malloc_uncollectable has semantics similar to standard malloc, but allocates objects that are traced by the collector.)

In our compiler, we sometimes stored the gc allocated memory pointer inside data structures allocated by the standard malloc. Therefore, when the gc started to collect memory, it would mistakenly collect the memory region referenced by these pointers. It explained why it only appeared after thousands of iterations. The memory was used a lot after thousands of iterations so the gc started to work. In addition, I tried to manually invoke GC_gcollect when GC_malloc was invoked. The segfault started to show in the first several iterations. This behavior also confirmed my hypothesis.

However, this type of problem cannot be easily solved by replacing malloc with GC_malloc in our codebase. I tried but failed to do so, because we also stored the pointer to the memory allocated by the gc in other places: memory allocated by std containers such as std::unordered_map and memory allocated by our compiler itself such as dispatch vector, class info, and etc.

  • To resolve the first type of memory, I found the following from bdwgc docs. We could simply include gc_allocator.h, which would override the default allocator in cpp.

Very often it will also be necessary to use gc_allocator.h and the allocator declared there to construct STL data structures. Otherwise subobjects of STL data structures will be allocated using a system allocator, and objects they refer to may be prematurely collected.

  • For the second one, it is much more complicated. We have to instruct the gc how to find the memory region created by our compiler. There is a function GC_add_roots(low_addr, high_addr_plus_1) in bdwgc for this purpose. However, I have no idea how to get the memory region created by our compiler.

In conclusion, this issue is not related to the newly added multithreading support, and I believe it requires some non-trivial work to fix this problem.

guoyiteng avatar Dec 20 '19 08:12 guoyiteng

Thanks for the digging Yiteng, I've update the issue title to reflect the actual problem.

dz333 avatar Dec 20 '19 15:12 dz333

It sounds like the problem is pointers from data structures that the compiler statically allocates? I would think that a ctor section could be generated to report such data structures to the GC via the call you describe

andrewcmyers avatar Dec 20 '19 16:12 andrewcmyers

Yes, I believe this is a reasonable way to resolve this problem.

guoyiteng avatar Dec 22 '19 20:12 guoyiteng