Skip to content

Commit

Permalink
Added malloc_trim() calls to Python allocator so RSS will decrease wh…
Browse files Browse the repository at this point in the history
…en memory is freed

This partially fixes #10088.  However we still have a global map that does not shrink, which can still create the appearance of leaking memory, as it will not be freed until the module is unloaded.

PiperOrigin-RevId: 562976233
  • Loading branch information
haberman authored and copybara-github committed Sep 6, 2023
1 parent dcc1f61 commit b5acf26
Showing 1 changed file with 48 additions and 2 deletions.
50 changes: 48 additions & 2 deletions upb/python/protobuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
#include "python/repeated.h"
#include "python/unknown_fields.h"

static upb_Arena* PyUpb_NewArena(void);

static void PyUpb_ModuleDealloc(void* module) {
PyUpb_ModuleState* s = PyModule_GetState(module);
PyUpb_WeakMap_Free(s->obj_cache);
Expand Down Expand Up @@ -125,7 +127,7 @@ struct PyUpb_WeakMap {
};

PyUpb_WeakMap* PyUpb_WeakMap_New(void) {
upb_Arena* arena = upb_Arena_New();
upb_Arena* arena = PyUpb_NewArena();
PyUpb_WeakMap* map = upb_Arena_Malloc(arena, sizeof(*map));
map->arena = arena;
upb_inttable_init(&map->table, map->arena);
Expand Down Expand Up @@ -224,10 +226,54 @@ typedef struct {
upb_Arena* arena;
} PyUpb_Arena;

// begin:google_only
// static upb_alloc* global_alloc = &upb_alloc_global;
// end:google_only

// begin:github_only
#ifdef __GLIBC__
#include <malloc.h> // malloc_trim()
#endif

// A special allocator that calls malloc_trim() periodically to release
// memory to the OS. Without this call, we appear to leak memory, at least
// as measured in RSS.
//
// We opt not to use this instead of PyMalloc (which would also solve the
// problem) because the latter requires the GIL to be held. This would make
// our messages unsafe to share with other languages that could free at
// unpredictable
// times.
static void* upb_trim_allocfunc(upb_alloc* alloc, void* ptr, size_t oldsize,
size_t size) {
(void)alloc;
(void)oldsize;
if (size == 0) {
free(ptr);
#ifdef __GLIBC__
static int count = 0;
if (++count == 10000) {
malloc_trim(0);
count = 0;
}
#endif
return NULL;
} else {
return realloc(ptr, size);
}
}
static upb_alloc trim_alloc = {&upb_trim_allocfunc};
static const upb_alloc* global_alloc = &trim_alloc;
// end:github_only

static upb_Arena* PyUpb_NewArena(void) {
return upb_Arena_Init(NULL, 0, global_alloc);
}

PyObject* PyUpb_Arena_New(void) {
PyUpb_ModuleState* state = PyUpb_ModuleState_Get();
PyUpb_Arena* arena = (void*)PyType_GenericAlloc(state->arena_type, 0);
arena->arena = upb_Arena_New();
arena->arena = PyUpb_NewArena();
return &arena->ob_base;
}

Expand Down

0 comments on commit b5acf26

Please sign in to comment.