Embeddable Kotlin compiler & memory leaks

Hi, I want to have scripting support in my server Kotlin(JVM) application, but it suffers from “memory leaks”.

My “compiler” compiles functions specified by input source code and returns interface which can be used in application. Such interface is used multiple times.

expected usage:

	val mapper: Function<Int, String> = compiler.getMapper<Int, String>(
		"return \"\$input,\$input\""
	) // this will compile the function
	val result = mapper.apply(123) // "123,123" // this will evaluate compiled function

I initialize scripting engine in this way:

	val se = KotlinJsr223JvmLocalScriptEngineFactory().scriptEngine
	val b = SimpleBindings()
	b["cls"] = holder
	se.setBindings(b, ScriptContext.ENGINE_SCOPE)

I let the script engine compile the class and return instance via bindings (pseudo code bellow)

Script:

class DYNAMICALLY_COMPILED_CLASS: Function<Int, String> {
		fun apply(val:Int) : String { return "$input,$input"; }
	}

	bindings["cls"].obj = DYNAMICALLY_COMPILED_CLASS() // return instance of the class via bindings. Caller will set bindings["cls"].obj to null after reading the instance

I tried to compile such functions in a loop and I watched “Used Heap” in VisualVM - heap usage is rising during compilation and even after clicking on “Perform GC”, heap usage is not returned to original value.
I personally think that this is not “bug”, but rather some unwanted classloader caching.

I tried experiments by setting my own classloader Thread.currentThread().contextClassLoader, but without success. I also tried calling .dispose()/.history.reset() on relevant stage: AggregatedReplStageState, but also without success.

Memory leaking is not so horrible, but definitely is not good for long-running application.
My current solution is to have the code in separated process and restart the app from time to time.

Do you have any suggestions, how to solve this memory issue / clean memory?

Thank you

Kotlin version: 1.3.61

The JSR-223 engine is a REPL, therefore it keeps previous state and compiled classes in memory by design. To reset these “leaks” you need to take the new engine, and let the other to be collected.

Hi @ilya.chernikov,

I’ve tried so many things, including creating a fresh engine for each eval. I forked a simple example on github. If you start this project up on localhost and open the Test.jmx file in Apache JMeter and run it, you will see that the response times get slower and slower with each iteration and the app will eventually get an out-of-memory exception. I’ve tried debugging down into the REPL engine, but I’m really struggling. I could really use your expertise here. I’d so much appreciate it! I really want to utilize this feature on one of my projects.

https://github.com/twferrell/klever