diff --git a/src/eval.c b/src/eval.c index 47989a0ac..7a8bdc8df 100644 --- a/src/eval.c +++ b/src/eval.c @@ -264,15 +264,31 @@ void scriptingInit(int setup) { lctx.lua = lua; } +/* Free lua_scripts dict and close lua interpreter. */ +void freeLuaScriptsSync(dict *lua_scripts, lua_State *lua) { + dictRelease(lua_scripts); + lua_close(lua); + +#if !defined(USE_LIBC) + /* The lua interpreter may hold a lot of memory internally, and lua is + * using libc. libc may take a bit longer to return the memory to the OS, + * so after lua_close, we call malloc_trim try to purge it earlier. + * + * We do that only when Redis itself does not use libc. When Lua and Redis + * use different allocators, one won't use the fragmentation holes of the + * other, and released memory can take a long time until it is returned to + * the OS. */ + zlibc_trim(); +#endif +} + /* Release resources related to Lua scripting. * This function is used in order to reset the scripting environment. */ void scriptingRelease(int async) { if (async) - freeLuaScriptsAsync(lctx.lua_scripts); + freeLuaScriptsAsync(lctx.lua_scripts, lctx.lua); else - dictRelease(lctx.lua_scripts); - lctx.lua_scripts_mem = 0; - lua_close(lctx.lua); + freeLuaScriptsSync(lctx.lua_scripts, lctx.lua); } void scriptingReset(int async) { diff --git a/src/lazyfree.c b/src/lazyfree.c index 5344c24ab..4498c9a36 100644 --- a/src/lazyfree.c +++ b/src/lazyfree.c @@ -42,8 +42,9 @@ void lazyFreeTrackingTable(void *args[]) { /* Release the lua_scripts dict. */ void lazyFreeLuaScripts(void *args[]) { dict *lua_scripts = args[0]; + lua_State *lua = args[1]; long long len = dictSize(lua_scripts); - dictRelease(lua_scripts); + freeLuaScriptsSync(lua_scripts, lua); atomicDecr(lazyfree_objects,len); atomicIncr(lazyfreed_objects,len); } @@ -195,13 +196,14 @@ void freeTrackingRadixTreeAsync(rax *tracking) { } } -/* Free lua_scripts dict, if the dict is huge enough, free it in async way. */ -void freeLuaScriptsAsync(dict *lua_scripts) { +/* Free lua_scripts dict, if the dict is huge enough, free it in async way. + * Close lua interpreter, if there are a lot of lua scripts, close it in async way. */ +void freeLuaScriptsAsync(dict *lua_scripts, lua_State *lua) { if (dictSize(lua_scripts) > LAZYFREE_THRESHOLD) { atomicIncr(lazyfree_objects,dictSize(lua_scripts)); - bioCreateLazyFreeJob(lazyFreeLuaScripts,1,lua_scripts); + bioCreateLazyFreeJob(lazyFreeLuaScripts,2,lua_scripts,lua); } else { - dictRelease(lua_scripts); + freeLuaScriptsSync(lua_scripts, lua); } } diff --git a/src/server.h b/src/server.h index fb0f27179..09ab74075 100644 --- a/src/server.h +++ b/src/server.h @@ -3381,7 +3381,8 @@ void ldbKillForkedSessions(void); int ldbPendingChildren(void); sds luaCreateFunction(client *c, robj *body); void luaLdbLineHook(lua_State *lua, lua_Debug *ar); -void freeLuaScriptsAsync(dict *lua_scripts); +void freeLuaScriptsSync(dict *lua_scripts, lua_State *lua); +void freeLuaScriptsAsync(dict *lua_scripts, lua_State *lua); void freeFunctionsAsync(functionsLibCtx *lib_ctx); int ldbIsEnabled(void); void ldbLog(sds entry); diff --git a/src/zmalloc.c b/src/zmalloc.c index fff48fbe0..56330ff1c 100644 --- a/src/zmalloc.c +++ b/src/zmalloc.c @@ -50,7 +50,6 @@ void zlibc_free(void *ptr) { } #include -#include #include "zmalloc.h" #include "atomicvar.h" @@ -757,6 +756,15 @@ int jemalloc_purge(void) { #endif +/* This function provides us access to the libc malloc_trim(). */ +void zlibc_trim(void) { +#if defined(__GLIBC__) && !defined(USE_LIBC) + malloc_trim(0); +#else + return; +#endif +} + #if defined(__APPLE__) /* For proc_pidinfo() used later in zmalloc_get_smap_bytes_by_field(). * Note that this file cannot be included in zmalloc.h because it includes diff --git a/src/zmalloc.h b/src/zmalloc.h index 1b63d6aed..461d0d1ac 100644 --- a/src/zmalloc.h +++ b/src/zmalloc.h @@ -71,6 +71,7 @@ */ #ifndef ZMALLOC_LIB #define ZMALLOC_LIB "libc" +#define USE_LIBC 1 #if !defined(NO_MALLOC_USABLE_SIZE) && \ (defined(__GLIBC__) || defined(__FreeBSD__) || \ @@ -93,6 +94,11 @@ #endif #endif +/* Includes for malloc_trim(), see zlibc_trim(). */ +#if defined(__GLIBC__) && !defined(USE_LIBC) +#include +#endif + /* We can enable the Redis defrag capabilities only if we are using Jemalloc * and the version used is our special version modified for Redis having * the ability to return per-allocation fragmentation hints. */ @@ -130,6 +136,7 @@ size_t zmalloc_get_private_dirty(long pid); size_t zmalloc_get_smap_bytes_by_field(char *field, long pid); size_t zmalloc_get_memory_size(void); void zlibc_free(void *ptr); +void zlibc_trim(void); void zmadvise_dontneed(void *ptr); #ifdef HAVE_DEFRAG