@@ -289,3 +289,76 @@ To run the JVM configuration:
289
289
To run the Native Image configuration:
290
290
291
291
mx --env ../../graal/vm/mx.vm/ce --exclude-components=slgm --dynamicimports /vm benchmark meso:nbody3 -- --python-vm=graalpython --jvm=graalvm-ce-python --jvm-config=native --python-vm-config=default --
292
+
293
+ ### Finding Memory Leaks
294
+
295
+ For best performance we keep references to long-lived user objects (mostly
296
+ functions, classes, and modules) directly in the AST nodes when using the
297
+ default configuration of a single Python context (as is used when running the
298
+ launcher). For better sharing of warmup and where absolutely best peak
299
+ performance is not needed, contexts can be configured with a shared engine and
300
+ the ASTs will be shared across contexts. However, that implies we * must* not
301
+ store any user objects strongly in the ASTs. Here is a query to find any such
302
+ leaks where a PythonObject is reacheable from any kind of Truffle AST Node
303
+ subinstance in VisualVM:
304
+
305
+ findLeaks("com.oracle.graal.python.builtins.objects.object.PythonObject", "com.oracle.truffle.api.nodes.Node")
306
+
307
+ function findLeaks(to, from) {
308
+ var objs = heap.objects(to, true)
309
+ var leaks = []
310
+ var path = []
311
+ var cutOffPath = false
312
+
313
+ while (objs.hasMoreElements() && leaks.length < 100) {
314
+ var o = objs.nextElement()
315
+ path = []
316
+ cutOffPath = false
317
+ if (isReferencedFromAtMaxDepth(o, 20)) {
318
+ if (!cutOffPath) {
319
+ leaks.push(o)
320
+ }
321
+ }
322
+ }
323
+ return leaks
324
+
325
+ function isReferencedFromAtMaxDepth(obj, limit) {
326
+ var refs = referrers(obj)
327
+ while (refs.hasMoreElements()) {
328
+ var o = refs.nextElement()
329
+ var refClass = classof(o)
330
+ var cutOffHere = false
331
+ while (refClass != null) {
332
+ if (refClass.name == from) {
333
+ leaks.push(classof(o).name)
334
+ return true
335
+ } else if (refClass.name == "java.lang.ref.WeakReference" ||
336
+ refClass.name == "java.lang.ref.SoftReference" ||
337
+ refClass.name == "java.lang.ref.PhantomReference") {
338
+ // any weak reference is fine
339
+ return false;
340
+ } else if (refClass.name == to) {
341
+ // we have found another `to` object along the path, use this one as the shorter path
342
+ cutOffHere = true
343
+ }
344
+ refClass = refClass.superclass
345
+ }
346
+ if (limit > 0) {
347
+ if (isReferencedFromAtMaxDepth(o, limit - 1)) {
348
+ if (!cutOffPath) {
349
+ if (cutOffHere) {
350
+ cutOffPath = true
351
+ leaks.push(o)
352
+ } else {
353
+ leaks.push(classof(o).name)
354
+ }
355
+ }
356
+ return true
357
+ }
358
+ }
359
+ }
360
+ return false
361
+ }
362
+ }
363
+
364
+ Running such a query on a multi-context heap should yield no results.
0 commit comments