The problem is : the memory is duplicated when forking and calling GC in 2.2.1. The main issue with this is that when operating on huge data, ranging to 3GB, my machine is killed just after one fork.
We have written a small program that reproduces the issue (see attached file).
The program instantiates an object and then forks into two processes. The GC is called in the child process. The memory allocation (as given by /proc/pid/smaps) changes from shared to private, thereby indicating a doubling of memory consumption.
Here is the output of the program (size is in mb):
https://bugs.ruby-lang.org/issues/10559#change-50245
We have tested the program on Ubuntu 14.04 with ruby 1.9.3, 2.1.3 and 2.2.1 (latest). The tests have been performed on a freshly installed Ubuntu machine, so no other software was getting involved.
We have also tried to fork 10 children and see a 10 doubling of the memory consumption, the issue only occurs after running the GC.
The question : what is the source of this problem (Ruby, kernel, Ubuntu) and what can be done with it ?
The source code to the program can be found here : https://bugs.ruby-lang.org/issues/10559#change-50245
EDIT :
Here is the code I work on.
def memory_object( size)
count = size/20
count.times do
result << "%20.18f" % rand
end
return result
end
big_memory = memory_object( 1000 * 1000* 30)
pid = fork do
big_memory_1 = memory_object( 1000 * 1000*30)
sleep( 1)
STDOUT.flush
exit!
end
Process.wait( pid)
If you run the above code and monitor the shared memory using smaps on linux, you will see that the moment the new object is created in the child process, the whole memory is becoming private but it shouldn't because I am not even modifying the original object. What is interesting, however, is that if the strings appended to the array are created like below, everything behaves normally.
result << rand.to_s
I suspect that it is because the bottom version creates less garbage. But even if I force the GC before the fork several times, it still happens. I suspect that it is because everything is allocated on the GC heap, which is actually modified and this causes the copy-on-write. Could it be possible ?