ALL software version info
- Windows OS
- Google Chrome
- bokeh - 2.3.2
- panel 0.11.3
- numpy 1.19.2
- pandas 1.2.5
- hvplot 0.7.1
- holoviews 1.14.3
- Pympler 0.9
Description of expected behavior and the observed behavior
We are trying to use classes that derive from panel components to create our own custom panels/components. We are running into a problem with a memory leak when using hv.tables and potentially with other components/classes. Let us know if there is an issue with the way we are implementing these libraries into our code or if there is actually a memory leak with holoviews holding on to references after a component is deleted. @philippjfr might have something to do with - Bokeh server getting killed by memory leak, and sometimes also getting Getting an "extra unexpected referrers!" error - #3 by Bryan - Community Support - Bokeh Discourse
Complete, minimal, self-contained example code that reproduces the issue
import gc
import holoviews as hv
import numpy as np
import panel as pn
import pandas as pd
import hvplot.pandas
from collections import OrderedDict as odict
from panel.template import DarkTheme, DefaultTheme
from pympler import tracker
hv.extension('bokeh')
pn.extension(sizing_mode='stretch_width')
class Application():
hv.DynamicMap.cache_size = 0
main_cmap = ['#265e3b','#326745','#3d7150','#497b5b','#548566','#608e71','#6b987c','#77a388','#83ad93','#8fb79f'] #Theme Light Blues/Greens
def __init__(self, title, light_mode=True):
if light_mode:
self.main_page = pn.template.MaterialTemplate(title=title, theme=DefaultTheme)
else:
self.main_page = pn.template.MaterialTemplate(title=title, theme=DarkTheme)
self.main_page.sidebar.append("\nBaby shark, doo doo doo doo doo doo\n"
"\nBaby shark, doo doo doo doo doo doo\n"
"\nBaby shark, doo doo doo doo doo doo\n"
"\nBaby shark!\n\n"
"\nMommy shark, doo doo doo doo doo doo\n"
"\nMommy shark, doo doo doo doo doo doo\n"
"\nMommy shark, doo doo doo doo doo doo\n"
"\nMommy shark!\n\n"
"\nDaddy shark, doo doo doo doo doo doo\n"
"\nDaddy shark, doo doo doo doo doo doo\n"
"\nDaddy shark, doo doo doo doo doo doo\n"
"\nDaddy shark!\n\n"
"\nGrandma shark, doo doo doo doo doo doo\n"
"\nGrandma shark, doo doo doo doo doo doo\n"
"\nGrandma shark, doo doo doo doo doo doo\n"
"\nGrandma shark!\n\n"
"\nGrandpa shark, doo doo doo doo doo doo\n"
"\nGrandpa shark, doo doo doo doo doo doo\n"
"\nGrandpa shark, doo doo doo doo doo doo\n"
"\nGrandpa shark!\n\n"
"\nLet’s go hunt, doo doo doo doo doo doo\n"
"\nLet’s go hunt, doo doo doo doo doo doo\n"
"\nLet’s go hunt, doo doo doo doo doo doo\n"
"\nLet’s go hunt!\n\n")
self.delete_button = pn.widgets.Button(name='Delete', button_type='danger', disabled=True)
self.delete_button.on_click(self.delete)
self.view_button = pn.widgets.Button(name='View Data', button_type='success', disabled=False)
self.view_button.on_click(self.view)
self.info = InfoPanel(CustomTab(self.view_button, self.delete_button))
self.main_page.main.append(self.info)
def delete(self, event):
"""Delete button callback to delete a selected experiment."""
self.info[-1] = CustomTab(self.view_button, self.delete_button)
self.delete_button.disabled = True
self.view_button.disabled = False
self.show_memory_usage()
def view(self, event):
"""Save selected experiment."""
#Initializing the meta data
self.info[0].add_tab(('Testing View', pn.Card(CustomPanel(self.select_data(), 'Test Plots', self.main_cmap), title='Data View')))
self.delete_button.disabled = False
self.view_button.disabled = True
self.show_memory_usage()
def select_data(self, num=1000000):
np.random.seed(1)
dists = {cat: pd.DataFrame(odict([('x',np.random.normal(x,s,num)),
('y',np.random.normal(y,s,num)),
('val',val),
('cat',cat)]))
for x, y, s, val, cat in
[( 2, 2, 0.03, 10, "d1"),
( 2, -2, 0.10, 20, "d2"),
( -2, -2, 0.50, 30, "d3"),
( -2, 2, 1.00, 40, "d4"),
( 0, 0, 3.00, 50, "d5")] }
df = pd.concat(dists,ignore_index=True)
df["cat"] = df["cat"].astype("category")
return df
def show_memory_usage(self):
mem = tracker.SummaryTracker()
memory = pd.DataFrame(mem.create_summary(), columns=['object', 'number_of_objects', 'memory'])
memory['mem_per_object'] = memory['memory'] / memory['number_of_objects']
memory['memory'] = round(memory['memory'] / 1000000, 2)
print('\n####################################### Memory Allocation ##########################################')
print(memory.sort_values('memory', ascending=False).head(10))
print()
print(f"Total Memory - {memory['memory'].sum()} MB")
print('####################################################################################################\n')
print(gc.collect()) # prints number of unreachable objects
class CustomPanel(pn.Column):
def __init__(self, df, name, main_cmap, *objects, **params):
run_meta_columns = ['val', 'x', 'y']
plot = df.hvplot.scatter(x='x', y='y', title='Original',
cmap=main_cmap, grid=True, datashade=True, width=800, height=600).opts(bgcolor='lightgrey')
super().__init__(plot, SelectionTablePanel(df, columns=['cat', *run_meta_columns]), name=name, *objects, **params) #induces memory leak
# super().__init__(plot, name=name, *objects, **params) #no memory leak
class InfoPanel(pn.Column):
def __init__(self, init_panel, *objects, **params):
super().__init__(init_panel, *objects, **params)
class CustomTab(pn.Tabs):
def __init__(self, view_button, delete_button, *objects, **params):
init_tab = ('Overview', pn.Row(view_button, delete_button))
super().__init__(init_tab, dynamic=False, *objects, **params)
def refresh_tabs(self, tabs):
self.clear()
self = tabs
def add_tab(self, panel):
self.append(panel)
def remove_tab(self, index):
self.pop(index)
class SelectionTablePanel(pn.Row):
def __init__(self, df, columns=None, sort_column='', *args, **kwargs):
table_df = df[columns]
if sort_column != '':
table_df = table_df.sort_values(sort_column, ascending=False).reset_index(drop=True)
super().__init__(hv.Table(table_df).opts(hooks=[self.hide_index]), *args, **kwargs)
def hide_index(self, plot, element):
plot.handles['table'].index_position = None
#app = Application(title='Memory Leak', light_mode=True).main_page
app = Application(title='Memory Leak', light_mode=False).main_page
app.servable()
Stack traceback and/or browser JavaScript console output
bokeh.document.document - ERROR - Module <module ‘bokeh_app_f972585d1ef142c1ab049361ea8dff71’ from ‘{app-file-path}’> has extra unexpected referrers! This could indicate a serious memory leak. Extra referrers: [<cell at 0x000001F13D878100: module object at 0x000001F13D8A7B80>]