Difference between revisions of "InvalidMemoryOperationError"
(Created page with "An <tt>InvalidMemoryOperationError</tt> can occur when an allocation occurs during the execution of the garbage collector. This can happen when the garbage collector runs, fin...") |
(No difference)
|
Revision as of 00:42, 25 January 2015
An InvalidMemoryOperationError can occur when an allocation occurs during the execution of the garbage collector. This can happen when the garbage collector runs, finds an unreferenced class instance, calls its finalizer (class destructor, ~this), which in turn does something which attempts to allocate memory. Since the current implementation of the D garbage collector is not re-entrant, this causes a fatal error.
InvalidMemoryOperationError problems are difficult to debug because they do not print a stack trace, and may not occur reliably. This page describes how to find the cause of these errors.
Example test case
A simple program which will throw an InvalidMemoryOperationError:
class C
{
~this()
{
new ubyte[1024];
}
}
void main()
{
foreach (n; 0..100)
new C;
import core.memory;
GC.collect();
}
Debugging
First, you will need to build Druntime with debug information (-g) and stack frames (-gs) enabled. Without stack frames in Druntime, you will not be able to get a reliable stack trace. D version 2.067 or newer is also required, as in older versions the onInvalidMemoryOperationError function is inlined, thus making it impossible to breakpoint.
The easiest way to do this is is to use Digger:
digger build master+CyberShadow/druntime/debug+CyberShadow/phobos/debug
This will build the latest version of D from GitHub, and pull in two branches from my fork with the necessary Makefile changes.
Once you got your project to build with the latest compiler version, see if the error still occurs - it might have been resolved by a change in D. Otherwise, build your program with -g (and -gs if you have to use -release), and continue to the section corresponding to your platform.
Windows
The easiest way to debug this problem on Windows is to use Visual Studio's debugger.
- Start Visual Studio, go to File → Open → Project/Solution..., and select your compiled .exe file.
- In the Solution Explorer pane, right click the project (the name of your program), and select Debug → Step Into new instance
- Press Ctrl+B to open the New Breakpoint dialog. In the Function field, type _onInvalidMemoryOperationError. Click OK.
- Click Continue to start your program.
Your program should now break at the _onInvalidMemoryOperationError function.
Open the stack trace view. It might look like this:
> test.exe!core.exception.onInvalidMemoryOperationError(...) Line 531 test.exe!gc.gc.GC.malloc(...) Line 471 test.exe!gc.proxy.gc_qalloc(...) Line 217 + 0x14 bytes test.exe!rt.lifetime.__arrayAlloc(...) Line 440 + 0x10 bytes test.exe!rt.lifetime._d_newarrayU(...) Line 940 test.exe!rt.lifetime._d_newarrayT(...) Line 957 + 0x9 bytes test.exe!test.C.~this() Line 5 + 0x10 bytes test.exe!rt.lifetime.rt_finalize2(...) Line 1403 test.exe!rt.lifetime.rt_finalizeFromGC(...) Line 1434 + 0xa bytes test.exe!gc.gc.Gcx.sweep() Line 2616 + 0x15 bytes test.exe!gc.gc.Gcx.fullcollect() Line 2532 + 0x7 bytes test.exe!gc.gc.GC.fullCollect() Line 1177 + 0xb bytes test.exe!gc.proxy.gc_collect() Line 171 + 0xa bytes test.exe!core.memory.GC.collect() Line 170 test.exe!D main() Line 15 + 0x5 bytes ...
Note the bolded line: it indicates the cause of our problem, the allocation inside our destructor.
Linux
On Linux, you can use gdb to breakpoint onInvalidMemoryOperationError:
$ dmd -g program $ gdb ./program GNU gdb (Ubuntu 7.7-0ubuntu3) 7.7 Copyright (C) 2014 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./program...done. (gdb) start Temporary breakpoint 1 at 0x401d64 Starting program: /home/.../program [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". Temporary breakpoint 1, 0x0000000000401d64 in main () (gdb) break onInvalidMemoryOperationError Breakpoint 2 at 0x415d20: file src/core/exception.d, line 536. (gdb) cont Continuing. Breakpoint 2, onInvalidMemoryOperationError (pretend_sideffect=0x40e591 <gc.gc.Gcx.mark()+185>) at src/core/exception.d:536 536 cast(void*) typeid(InvalidMemoryOperationError).init; (gdb) where #0 onInvalidMemoryOperationError (...) at src/core/exception.d:536 #1 0x000000000040b0c2 in gc.gc.GC.malloc() (...) at src/gc/gc.d:469 #2 0x0000000000402f34 in gc_qalloc (...) at src/gc/proxy.d:217 #3 0x0000000000407552 in rt.lifetime.__arrayAlloc() (...) at src/core/memory.d:366 #4 0x00000000004076c5 in _d_newarrayU (...) at src/rt/lifetime.d:939 #5 0x0000000000403ddd in _d_newarrayT (...) at src/rt/lifetime.d:957 #6 0x0000000000401d15 in program.C.__dtor() (...) at program.d:5 #7 0x00000000004192d0 in rt_finalize2 (...) at src/rt/lifetime.d:1400 #8 0x000000000041129a in rt_finalizeFromGC (...) at src/rt/lifetime.d:1434 #9 0x000000000040f002 in gc.gc.Gcx.sweep() (...) at src/gc/gc.d:2390 #10 0x000000000040f6bf in gc.gc.Gcx.fullcollect() (...) at src/gc/gc.d:2532 #11 0x000000000040c453 in gc.gc.GC.fullCollect() (...) at src/gc/gc.d:1177 #12 0x0000000000402db7 in gc_collect () at src/gc/proxy.d:171 #13 0x0000000000402a59 in core.memory.GC.collect() () at src/core/memory.d:169 #14 0x0000000000401d55 in D main () at program.d:15 ...
As above, the bolded line (between rt_finalize2 and _d_newarrayT) indicates the problem.