This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Starting a second multiprocessing.Manager causes INCREF on all object created by the first one.
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.2, Python 2.7
process
Status: closed Resolution: wont fix
Dependencies: Superseder:
Assigned To: Nosy List: mythsmith, r.david.murray, sbt
Priority: normal Keywords:

Created on 2014-02-17 15:47 by mythsmith, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
output.txt mythsmith, 2014-02-17 15:47 Output of the script reproducing the problem
Messages (6)
msg211421 - (view) Author: mythsmith (mythsmith) Date: 2014-02-17 15:47
I seems that upon the start of a second manager, all objects referenced in the first one gets an INCREF. On the third start, all objects created by the first and the second manager get another INCREF. And so on. I cannot understand why the start of a totally new manager, in a new process, will cause all these INCREF about object hosted on other managers - which cause the program to take forever to start/stop.

This small script fully reproduces the behaviour, tested in python 2.7 and 3.2:

from __future__ import print_function
import multiprocessing, logging
# # Activate multiprocessing logging
mplog = multiprocessing.get_logger()
mplog.setLevel(multiprocessing.util.DEBUG)
mplog.addHandler(logging.StreamHandler())
objs=[]
def newman(n=50):
    global objs
    m=multiprocessing.Manager()
    print('created')
    for i in range(n):
        objs.append(m.Value('i',i)) 
    return m    

print('#### first man')
m1=newman()

print ('#### second man')
m2=newman()

print ('#### third man')
m3=newman(0)

(Output is attached)

After the start of the first manager, the logger prints out the messages relative to the creation of the first 50 objects.

But when the second manager is starting - before any object was created by it - the logger prints out exactly 50 INCREF messages.Then follows the messages relating to the creation of the 50 new objects on manager 2.

When the third manager starts - before any object was created by it - 100 more INCREF messages are printed.

No object creation message is seen after m3 creation, as I passed 0 to the newman() function.

When the program ends, a similar amount of DECREF messages is printed.

It seems that, when I start a new manager, it creates a reference to all objects referenced by previous managers. In a big application this translates into extremely slow startup/shutdown.
msg211547 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2014-02-18 20:31
On Unix, using the fork start method (which was the only option till 3.4), every sub process will incref every shared object for which its parent has a reference.

This is deliberate because there is not really any way to know which shared objects a subprocess might use.  (On Windows where only things pickled as part of the process object are inherited by the child process, we can know exactly which shared objects the child process should incref.)

Typical programs will only have a single manager (or a very small number) but may have a large number of normal processes (which will also do the increfing).  I do not think that this is worth trying to fix, particularly as it can cause compatibility problems.

For 3.4 you can use the "spawn" or "forkserver" start methods instead.

import multiprocessing, logging

objs = []

def newman(n=50):
    m = multiprocessing.Manager()
    print('created')
    for i in range(n):
        objs.append(m.Value('i',i))
    return m

def foo():
    pass

if __name__ == '__main__':
    ## Try uncommenting next line with Python 3.4
    # multiprocessing.set_start_method('spawn')
    multiprocessing.log_to_stderr(logging.DEBUG)
    print('#### first man')
    m1 = newman()
    print('#### starting foo')
    p = multiprocessing.Process(target=foo)
    p.start()
    p.join()
msg211623 - (view) Author: mythsmith (mythsmith) Date: 2014-02-19 15:01
Thanks Richard. The set_start_method() call will affect any process started from that time on? Is it possible to change idea at some point in the future?
Anyway, I cannot upgrade right now.

Would it be an option to subclass BaseProxy, override the _after_fork method so it will do nothing upon forking?
msg211665 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2014-02-19 22:13
> Thanks Richard. The set_start_method() call will affect any process 
> started from that time on? Is it possible to change idea at some point in 
> the future?

You can use different start methods in the same program by creating different contexts:

  spawn_ctx = multiprocessing.get_context('spawn')
  manager = spawn_ctx.Manager()

> Anyway, I cannot upgrade right now.
>
> Would it be an option to subclass BaseProxy, override the _after_fork
> method so it will do nothing upon forking?

That would probably mean that proxy objects could not be inherited by *any* sub-process.  (You would then need to use some other way of sharing access between processes and to manage the lifetime of the shared object.)
msg211709 - (view) Author: mythsmith (mythsmith) Date: 2014-02-20 07:42
> That would probably mean that proxy objects could not be inherited by *any* sub-process. 

If I only avoid after-fork incref, I must be very careful at not deleting them in any subprocess, as this would cause a decref which was not compensated by the after-fork incref at the beginning of the subprocess. 

But as long as I never delete such objects in any subprocess, this should make no difference... Or will they get deleted whenever any of the subprocesses ends?
msg240343 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-04-09 15:41
Based on Richard's comments I'm closing this won't fix.  If someone comes up with a clever solution, we can reopen.
History
Date User Action Args
2022-04-11 14:57:58adminsetgithub: 64859
2015-04-09 15:41:58r.david.murraysetstatus: open -> closed

nosy: + r.david.murray
messages: + msg240343

resolution: wont fix
stage: resolved
2014-02-20 07:42:30mythsmithsetmessages: + msg211709
2014-02-19 22:13:15sbtsetmessages: + msg211665
2014-02-19 15:01:36mythsmithsetmessages: + msg211623
2014-02-18 20:31:52sbtsetmessages: + msg211547
2014-02-17 22:34:03ned.deilysetnosy: + sbt
2014-02-17 15:47:05mythsmithcreate