You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用on_event时Bokeh Server出现内存泄漏问题

问题原因与解决方案

核心问题:无限事件循环

你遇到的内存泄漏和崩溃,本质是无限事件循环导致的:

  • 用户缩放图表触发RangesUpdate事件后,回调函数更新了数据源的x和y数据
  • Bokeh会自动根据新数据调整图表的x轴范围,这又会再次触发RangesUpdate事件
  • 该过程反复执行,导致服务器持续处理事件、占用内存,最终崩溃
  • Jupyter环境的事件处理机制存在隐性节流,因此未触发这个循环

解决方法

方法1:更新数据时临时禁用事件监听

在回调函数中先移除事件绑定,更新数据后再重新绑定,避免循环触发:

from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource
from bokeh.events import RangesUpdate
from bokeh.server.server import Server
import numpy as np

def bkapp(doc):
    fig = figure()
    x = np.linspace(-6, 6)
    y = np.cos(x)
    source = ColumnDataSource({"x": x, "y": y})
    line = fig.line("x", "y", source=source)
    
    def ranges_update(event):
        # 临时移除事件监听
        fig.remove_on_event(RangesUpdate, ranges_update)
        # 更新数据(直接替换data比update更高效)
        x_new = np.linspace(event.x0, event.x1)
        y_new = np.cos(x_new)
        line.data_source.data = {"x": x_new, "y": y_new}
        # 重新绑定事件
        fig.on_event(RangesUpdate, ranges_update)
        
    fig.on_event(RangesUpdate, ranges_update)
    doc.add_root(fig)

server = Server(bkapp, num_procs=1)
server.start()
server.io_loop.add_callback(server.show, "/")
server.io_loop.start()

方法2:手动固定x轴范围,禁止自动调整

更新数据后手动设置x轴范围,阻止Bokeh自动调整范围触发新事件:

def ranges_update(event):
    x_new = np.linspace(event.x0, event.x1)
    y_new = np.cos(x_new)
    line.data_source.data = {"x": x_new, "y": y_new}
    # 手动设置x轴范围,覆盖自动调整
    fig.x_range.start = event.x0
    fig.x_range.end = event.x1

方法3:改用前端CustomJS回调(无需服务器参与)

如果逻辑不需要Python处理,直接用前端回调可完全避免服务器端循环问题:

from bokeh.models import CustomJS

def bkapp(doc):
    fig = figure()
    x = np.linspace(-6, 6)
    y = np.cos(x)
    source = ColumnDataSource({"x": x, "y": y})
    line = fig.line("x", "y", source=source)
    
    # 前端回调,直接在浏览器中执行
    callback = CustomJS(args=dict(source=source), code="""
        const x0 = cb_obj.x_range.start;
        const x1 = cb_obj.x_range.end;
        const x = Array.from({length: 50}, (_, i) => x0 + (x1 - x0)*i/49);
        const y = x.map(v => Math.cos(v));
        source.data = {x, y};
        // 手动固定范围
        cb_obj.x_range.start = x0;
        cb_obj.x_range.end = x1;
    """)
    fig.x_range.js_on_change('start', callback)
    fig.x_range.js_on_change('end', callback)
    
    doc.add_root(fig)

内容的提问来源于stack exchange,提问作者Davide_sd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 05:05:36