Memory issues with `multiprocessing` and GroupBy on multiindex DataFrame
#### Code Sample, a copy-pastable example if possible

```python
import pandas as pd
import multiprocessing as mp
import numpy as np
%load_ext memory_profiler
```


```python
def working_apply_function(x):
    """
    Working version of apply function
    """
    var_a = x["var_a"].values
    var_b = x["var_b"].values
    # Hit index is accessed as a column
    hit_idx = x["hit_index"].values
    # Normally some more complex function of var_a, var_b, and hit_idx
    interesting_hits = hit_idx[[np.argmax(var_a), np.argmin(var_b)]]
    return interesting_hits

def working_multiprocess(data_frame, n_proc=None):
    """
    Get the hit index of interesting hits in a given event group, working version
    """
    # Create a copy of the data with the columns that are needed
    grp_data = pd.DataFrame(data_frame[["var_a", "var_b"]])
    # Add the hit index as a column.  This index that forms the basis of the output
    grp_data.reset_index(level="hit_index", drop=False, inplace=True)
    # Group the data by the major index
    grp_data = grp_data.groupby("event_index")
    # Find the interesting hit indexes
    interesting_hit_idxs = None
    # Use sequential mode if only one cpu requested
    if n_proc is None:
        interesting_hit_idxs = grp_data.apply(working_apply_function)
        interesting_hit_idxs = interesting_hit_idxs.values
    # Open up a pool of CPUs otherwise
    else:
        # Open the pool
        with mp.Pool(n_proc) as pool:
            interesting_hit_idxs = pool.map(working_apply_function,
                                            [grp for _, grp in grp_data])
    # Return the correct indexes
    return np.sort(np.concatenate(interesting_hit_idxs))

def leaky_apply_function(x):
    """
    Leaky version of apply_function where indexes are passed
    """
    var_a = x["var_a"].values
    var_b = x["var_b"].values
    # Hit index is accessed as an index
    hit_idx = x.index.get_level_values("hit_index")
    # Normally some more complex function of var_a, var_b, and hit_idx
    interesting_hits = hit_idx[[np.argmax(var_a), np.argmin(var_b)]]
    return interesting_hits

def leaky_multiprocess(data_frame, n_proc=None):
    """
    Get the hit index of interesting hits in a given event group, leaky version
    """
    # Group by event, but pass in the hit_index as an index
    grp_data = data_frame[["var_a", "var_b"]].groupby("event_index")
    # Find the interesting hits
    interesting_hit_idxs = None
    # Use sequential mode if only one cpu requested
    if n_proc is None:
        interesting_hit_idxs = grp_data.apply(leaky_apply_function)
        interesting_hit_idxs = interesting_hit_idxs.dropna().values
        # Return None if there are no hit ids
        if interesting_hit_idxs.shape[0] == 0:
            return None
    # Open up a pool of CPUs otherwise
    else:
        # Open the pool
        with mp.Pool(n_proc) as pool:
            interesting_hit_idxs = pool.map(leaky_apply_function, [grp for _, grp in grp_data])
        # Return None if there are none
        if not interesting_hit_idxs:
            return None
    # Return the correct indexes
    return np.sort(np.concatenate(interesting_hit_idxs))
```


```python
# Define a large number of columns
n_hits = int(1e6)
hits_per_event = int(4e3)
n_events = n_hits//hits_per_event
print(n_events, n_hits, hits_per_event)
# Define the data frame
test_data_frame = pd.DataFrame(data={"var_a": np.random.random(size=n_hits),
                                     "var_b": np.random.random(size=n_hits)})
# Set the indexes as columns
test_data_frame["event_index"] = np.concatenate([np.repeat(evt, hits_per_event) for evt in range(n_events)])
test_data_frame["hit_index"] = np.arange(n_hits)
# Set them as indexes in place and drop them
test_data_frame.set_index(["event_index", "hit_index"], drop=True, inplace=True)
test_data_frame.info()
```

    250 1000000 4000
    <class 'pandas.core.frame.DataFrame'>
    MultiIndex: 1000000 entries, (0, 0) to (249, 999999)
    Data columns (total 2 columns):
    var_a    1000000 non-null float64
    var_b    1000000 non-null float64
    dtypes: float64(2)
    memory usage: 28.6 MB

```python
test_data_frame.head()
```


<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th></th>
      <th>var_a</th>
      <th>var_b</th>
    </tr>
    <tr>
      <th>event_index</th>
      <th>hit_index</th>
      <th></th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th rowspan="5" valign="top">0</th>
      <th>0</th>
      <td>0.564295</td>
      <td>0.019199</td>
    </tr>
    <tr>
      <th>1</th>
      <td>0.836201</td>
      <td>0.586676</td>
    </tr>
    <tr>
      <th>2</th>
      <td>0.660377</td>
      <td>0.218596</td>
    </tr>
    <tr>
      <th>3</th>
      <td>0.887197</td>
      <td>0.324807</td>
    </tr>
    <tr>
      <th>4</th>
      <td>0.454777</td>
      <td>0.479819</td>
    </tr>
  </tbody>
</table>
</div>




```python
# In sequencial mode, both work fine
%memit interest_idx_work_seq = working_multiprocess(test_data_frame, n_proc=None)
```

    > peak memory: 233.51 MiB, increment: 61.47 MiB



```python
%memit interest_idx_leak_seq = leaky_multiprocess(test_data_frame, n_proc=None)
```

    > peak memory: 205.43 MiB, increment: 17.32 MiB



```python
np.testing.assert_allclose(interest_idx_work_seq, interest_idx_leak_seq)
```


```python
# In parallel mode, the working version does not hurt the memory
%memit interest_idx_work_multiproc = working_multiprocess(test_data_frame, n_proc=1)
```

    > peak memory: 246.51 MiB, increment: 41.06 MiB



```python
# In parallel mode, the leaky version kills the memory, even with only one process!
%memit interest_idx_leak_multiproc = leaky_multiprocess(test_data_frame, n_proc=1)
```
```
    peak memory: 1509.04 MiB, increment: 1262.78 MiB
```

#### Problem description

Let me start by saying I am new to `pandas`, but have long since used `numpy`.  Pandas is great, and thank you for all your effort.  This issue may be less of a bug and more of a consequence of trying to parallelize pandas.  If so, I hope it is useful for others.

It seems that passing an indexed object from a multiindexed data frame that is grouped on one index eats up memory when `mulitprocessing` is used.  This may be because the entire remaining index is passed to the group.  This is normally returned as a view of this index, but I speculate that `multiprocessing` copies the entire index.

This suspicion lead me to the working version.  This issue is similar,  #19630, and provided the hint I needed to get to the solution.

Above, I've recreated a very basic version of my use-case.  This use-case has a multi-index dataframe with an `event` index that groups columns together, and a `hit` index that uniquely enumerates all rows.  Really, my `apply_func` function is much more complicated, but the idea is that it determines `hit` indexes of interesting hits, as defined by values in other columns.

Once this data is passed to `multiprocessing`, the memory usage skyrockets.  I tried to profile it using `%memit`, as above.  My actual usage can be seen here, the bump is from `leaky_multiprocess` with `n_proc=1`, peaks around 10GB.  The other calls are flat on this scale

![image](https://user-images.githubusercontent.com/5664063/44282182-236a7f00-a252-11e8-93c7-1e45a7fa76e2.png)

#### Expected Output

Similar memory performance in all four cases posted. Instead the last case eats around 8GB.

#### Output of ``pd.show_versions()``
```
<details>INSTALLED VERSIONS
------------------
commit: None
python: 3.6.5.final.0
python-bits: 64
OS: Linux    
OS-release: 4.17.8-041708-generic
machine: x86_64
processor: x86_64
byteorder: little
LC_ALL: None   
LANG: en_GB.UTF-8
LOCALE: en_GB.UTF-8
                
pandas: 0.23.4
pytest: 3.7.1 
pip: 10.0.1  
setuptools: 39.2.0
Cython: None  
numpy: 1.14.3
scipy: 1.1.0
pyarrow: None   
xarray: None
IPython: 6.4.0
sphinx: None   
patsy: None     
dateutil: 2.7.3
pytz: 2018.4  
blosc: None 
bottleneck: None
tables: 3.4.3    
numexpr: 2.6.5  
feather: None          
matplotlib: 2.2.2
openpyxl: None
xlrd: None
xlwt: None
xlsxwriter: None
lxml: None
bs4: None
html5lib: 1.0.1
sqlalchemy: None
pymysql: None
psycopg2: None
jinja2: 2.10
s3fs: None
fastparquet: None
pandas_gbq: None
pandas_datareader: None
```