The leak comes from the following lines of _PySys_EndInit(): --- PyObject *warnoptions = get_warnoptions(); if (warnoptions == NULL) return -1; SET_SYS_FROM_STRING_BORROW_INT_RESULT("warnoptions", warnoptions); PyObject *xoptions = get_xoptions(); if (xoptions == NULL) return -1; SET_SYS_FROM_STRING_BORROW_INT_RESULT("_xoptions", xoptions); --- It's not the first time that I have an issue with these attributes. The last reference weak caused by multiple interpreters was also related to this one if I recall correctly. See bpo-30598 and my commit 865de27dd79571a4a5c7a7d22a07fb909c4a9f8e.
Extract of my : "The problem is that warnoptions is stored in a C global variable *and* in sys.warnoptions of each interpreter. The ownership of this variable is unclear." Maybe we need a change similar to bpo-28411 (commit 86b7afdfeee77993fe896a2aa13b3f4f95973f16) which removed the "modules" field from Py_InterpreterState.
Moving warnoptions (and xoptions) out of PyInterpreterState seems like a good idea to me for the same reasons as applied to sys.modules. The case for these two is even stronger since they are only used in sysmodule.c.