It looks as though creating and shutting down an in-process kernel leaks a thread.
The InProcessKernelManager.start_kernel method creates an InProcessKernel, whose iopub_thread trait creates a new IOPubThread object (wrapping a Python-level thread). I'd expect the InProcessKernelManager.shutdown_kernel to call stop on that IOPubThread method.
This was causing issues in a large test suite that created and disposed of IPython kernels; we ended up with one new thread per test, with the thread overload eventually leading to failed inter-thread communication in the Python process