#### ALL software version info
- panel 1.7.5 and 1.8.0
- python 3.12 - container… environment
- bokeh - 3.7.3
#### Expected
When using pn.serve to serve a multi-page application, I would expect to go to one page and have a new session created. When I go to another page, I would expect either that session to be removed. Which it is depending on the culling period. However, I would expect that memory consumed by other sessions to be re allocated.
#### Observed
When using pn.serve to serve a multi-page application, after the session culling occurs, no memory is re allocated back to the service running. It feels like this could eventually cause an OOM if several different users access the same application and have several sessions - although the sessions are removed the memory is never reclaimed.
I could be missing something here so any thoughts help!
#### Complete, minimal, self-contained example code that reproduces the issue
```python
import panel as pn
from typing import Any
import json
import random
# Configure Panel with minimal extensions
pn.extension(notifications=True)
def generate_mock_data(num_items: int = 10000) -> list[dict[str, Any]]:
"""Generate a large list of mock data to simulate memory-heavy content."""
mock_data = []
for i in range(num_items):
mock_data.append({
"id": i,
"name": f"User {i}",
"email": f"user{i}@example.com",
"age": random.randint(18, 80),
"city": random.choice(["New York", "London", "Tokyo", "Paris", "Berlin", "Sydney"]),
"occupation": random.choice(["Engineer", "Designer", "Manager", "Analyst", "Developer"]),
"salary": random.randint(30000, 150000),
"active": random.choice([True, False]),
"metadata": {
"created_at": f"2023-{random.randint(1, 12):02d}-{random.randint(1, 28):02d}",
"last_login": f"2024-{random.randint(1, 12):02d}-{random.randint(1, 28):02d}",
"preferences": {
"theme": random.choice(["light", "dark"]),
"notifications": random.choice([True, False]),
"language": random.choice(["en", "es", "fr", "de"])
}
}
})
return mock_data
def create_page_a() -> Any:
"""Page factory A - simulates lazy page loading with session info access and large mock data."""
print("Page A factory called")
# Generate large mock data to exemplify memory-heavy page
mock_data = generate_mock_data(100000)
# Convert to JSON string for display (this will be large)
json_payload = json.dumps(mock_data, indent=2)
# Create a Panel layout with the data
data_pane = pn.pane.JSON(json_payload, name="Mock Data", height=600, sizing_mode="stretch_width")
return pn.Column(
pn.pane.Markdown("# Page A\nThis is page A content with large mock data."),
pn.pane.Markdown(f"**Total items:** {len(mock_data)}"),
data_pane,
sizing_mode="stretch_width"
)
def create_page_b() -> Any:
"""Page factory B - simulates lazy page loading with session info access."""
print("Page B factory called")
return pn.pane.Markdown("# Page B\nThis is page B content.")
def create_home_page() -> Any:
"""Home page factory."""
print("Home page factory called")
return pn.pane.Markdown("""
# Home Page
Navigate between pages to test session management:
- [Page A](/page-a)
- [Page B](/page-b)
""")
# Route mapping using lazy page factories
apps = {
"": create_home_page,
"page-a": create_page_a,
"page-b": create_page_b,
}
if __name__ == "__main__":
pn.state.on_session_created(lambda s: print(f"Session created: {s.__dict__}"))
pn.state.on_session_destroyed(lambda s: print(f"Session destroyed: {s.__dict__}"))
pn.serve(
apps, # type: ignore[arg-type]
address="0.0.0.0",
port=5007,
show=False,
autoreload=False,
dev=False,
warm=True,
allow_websocket_origin=["*"],
title="Session Management Repro",
reuse_session=True,
check_unused_sessions_milliseconds=5000, # Check every 5 seconds
unused_session_lifetime_milliseconds=5000, # Destroy after 5 seconds
)
```
```dockerfile
FROM python:3.12
WORKDIR "/app"
COPY . .
RUN pip install -r requirements.txt
CMD ["python3", "-m", "minimal_repro"]
```
- Docker Stats
<img width="802" height="68" alt="Image" src="https://github.com/user-attachments/assets/473c3711-6833-47e0-b127-4a77c094ff4c" />
I hit a certain point where the memory sort of plateaus if you will, but when sessions are destroyed, that memory is never re allocated back. Is this expected?
However if I open up localhost in several different tabs, memory just continues to increase and as mentioned, is never reclaimed.
<img width="791" height="63" alt="Image" src="https://github.com/user-attachments/assets/5362a2da-b92b-4d04-b48b-6b0150158699" />
Which in turns, means for long running applications they _eventually_ will hit an OOM.