- 适用于 JDK 23 的 GraalVM(最新)
- 适用于 JDK 24 的 GraalVM(抢先体验)
- 适用于 JDK 21 的 GraalVM
- 适用于 JDK 17 的 GraalVM
- 归档
- 开发版本
洞察手册
GraalVM 洞察是一个多功能、灵活的工具,用于编写可靠的应用程序。该工具的动态特性使您能够选择性地在现有应用程序上应用跟踪切入点,而不会损失性能。
任何具有中等技能的黑客都可以轻松创建所谓的洞察片段,并将它们动态应用于实际应用程序。这提供了对程序执行和行为的终极洞察,而不会影响其速度。
目录 #
- 快速入门
- 热门前十示例
- 将洞察应用于任何 GraalVM 语言
- JavaScript 洞察
- Python 洞察
- Ruby 洞察
- R 洞察
- C 代码洞察
- 检查值
- 修改局部变量
- 特定位置的洞察
- 延迟 Node.JS 中的洞察初始化
- 处理异常
- 拦截和更改执行
- 最小开销
- 访问局部变量时的最小开销
- 访问执行堆栈
- 关于 GraalVM 洞察 API 的说明
- 堆转储
快速入门 #
从必须的HelloWorld示例开始。创建一个名为source-tracing.js的脚本,内容如下
insight.on('source', function(ev) {
if (ev.characters) {
print(`Loading ${ev.characters.length} characters from ${ev.name}`);
}
});
使用 GraalVM 的node
启动器运行它,并添加--insight
工具选项。观察加载和评估的脚本
./bin/node --js.print --experimental-options --insight=source-tracing.js -e "print('The result: ' + 6 * 7)" | tail -n 10
Loading 29938 characters from url.js
Loading 345 characters from internal/idna.js
Loading 12642 characters from punycode.js
Loading 33678 characters from internal/modules/cjs/loader.js
Loading 13058 characters from vm.js
Loading 52408 characters from fs.js
Loading 15920 characters from internal/fs/utils.js
Loading 505 characters from [eval]-wrapper
Loading 29 characters from [eval]
The result: 42
刚刚发生了什么?GraalVM 洞察source-tracing.js脚本使用提供的insight
对象将源监听器附加到运行时。因此,每当node
加载脚本时,都会通知监听器,并且可以采取操作(在本例中,打印处理的脚本的长度和名称)。
热门前十示例 #
收集洞察信息并不局限于打印语句。您可以在语言中执行任何图灵完备的计算。例如,一个程序,它计算所有方法调用并将最频繁的调用在执行结束后转储。
将以下代码保存到function-hotness-tracing.js
var map = new Map();
function dumpHotness() {
print("==== Hotness Top 10 ====");
var count = 10;
var digits = 3;
Array.from(map.entries()).sort((one, two) => two[1] - one[1]).forEach(function (entry) {
var number = entry[1].toString();
if (number.length >= digits) {
digits = number.length;
} else {
number = Array(digits - number.length + 1).join(' ') + number;
}
if (count-- > 0) print(`${number} calls to ${entry[0]}`);
});
print("========================");
}
insight.on('enter', function(ev) {
var cnt = map.get(ev.name);
if (cnt) {
cnt = cnt + 1;
} else {
cnt = 1;
}
map.set(ev.name, cnt);
}, {
roots: true
});
insight.on('close', dumpHotness);
map
是一个全局变量,对整个洞察脚本可见,它使代码能够在insight.on('enter')
函数和dumpHotness
函数之间共享数据。当node
进程执行结束时(通过insight.on('close', dumpHotness)
注册)执行后者。运行程序
./bin/node --js.print --experimental-options --insight=function-hotness-tracing.js -e "print('The result: ' + 6 * 7)"
The result: 42
==== Hotness Top 10 ====
516 calls to isPosixPathSeparator
311 calls to :=>
269 calls to E
263 calls to makeNodeErrorWithCode
159 calls to :anonymous
157 calls to :program
58 calls to getOptionValue
58 calls to getCLIOptionsFromBinding
48 calls to validateString
43 calls to hideStackFrames
========================
当node
进程退出时,会打印一个包含函数调用名称和次数的表格。
将洞察应用于任何 GraalVM 语言 #
前面的示例是用 JavaScript 编写的,并使用了node
,但由于 GraalVM 的多语言特性,您可以使用相同的工具,并将它应用于 GraalVM 支持的任何语言。例如,使用 GraalVM 洞察测试 Ruby 语言。
首先,在source-trace.js文件中创建工具
insight.on('source', function(ev) {
if (ev.uri.indexOf('gems') === -1) {
let n = ev.uri.substring(ev.uri.lastIndexOf('/') + 1);
print('JavaScript instrument observed load of ' + n);
}
});
在helloworld.rb文件中准备您的 Ruby 程序
puts 'Hello from GraalVM Ruby!'
注意:确保启用了 Ruby 支持。请参阅多语言编程指南。
将 JavaScript 工具应用于 Ruby 程序。您应该看到以下内容
./bin/ruby --polyglot --insight=source-trace.js helloworld.rb
JavaScript instrument observed load of helloworld.rb
Hello from GraalVM Ruby!
有必要使用--polyglot
参数启动 GraalVM 的 Ruby 启动器,因为source-tracing.js脚本仍然是用 JavaScript 编写的。
JavaScript 洞察 #
如上一节所述,GraalVM 洞察并不局限于 Node.js。它在 GraalVM 提供的所有语言运行时中都可用。尝试 GraalVM 附带的 JavaScript 实现。
创建function-tracing.js脚本
var count = 0;
var next = 8;
insight.on('enter', function(ev) {
if (count++ % next === 0) {
print(`Just called ${ev.name} as ${count} function invocation`);
next *= 2;
}
}, {
roots: true
});
在sieve.js上运行它。它是一个示例脚本,它使用埃拉托斯特尼筛法的变体来计算十万个素数
./bin/js --insight=function-tracing.js sieve.js | grep -v Computed
Just called :program as 1 function invocation
Just called Natural.next as 17 function invocation
Just called Natural.next as 33 function invocation
Just called Natural.next as 65 function invocation
Just called Natural.next as 129 function invocation
Just called Filter as 257 function invocation
Just called Natural.next as 513 function invocation
Just called Natural.next as 1025 function invocation
Just called Natural.next as 2049 function invocation
Just called Natural.next as 4097 function invocation
Python 洞察 #
不仅可以为任何 GraalVM 语言编写工具,还可以使用该语言编写洞察脚本。在本节中,您将找到一个 Python 示例。
可以使用 Python 编写 GraalVM 洞察脚本。此类洞察可以应用于用 Python 或任何其他语言编写的程序。
以下是一个脚本示例,它在调用minusOne
函数时打印出变量n
的值。将此代码保存到agent.py文件
def onEnter(ctx, frame):
print(f"minusOne {frame.n}")
class At:
sourcePath = ".*agent-fib.js"
class Roots:
roots = True
at = At()
rootNameFilter = "minusOne"
insight.on("enter", onEnter, Roots())
此代码使用了在 GraalVM 22.2 中引入的源位置声明性规范。使用较旧的 GraalVM 版本使用动态sourceFilter
def onEnter(ctx, frame):
print(f"minusOne {frame.n}")
class Roots:
roots = True
rootNameFilter = "minusOne"
def sourceFilter(self, src):
return src.name == "agent-fib.js"
insight.on("enter", onEnter, Roots())
使用以下命令将此脚本应用于agent-fib.js
`./bin/js --polyglot --insight=agent.py agent-fib.js`
注意:确保启用了 Python 支持。请参阅多语言编程指南。
Ruby 洞察 #
可以使用 Ruby 编写 GraalVM 洞察脚本。此类洞察可以应用于用 Ruby 或任何其他语言编写的程序。
注意:确保启用了 Ruby 支持。请参阅多语言编程指南。
创建source-tracing.rb脚本
puts("Ruby: Insight version #{insight.version} is launching")
insight.on("source", -> (env) {
puts "Ruby: observed loading of #{env.name}"
})
puts("Ruby: Hooks are ready!")
启动 Node.js 程序,并使用 Ruby 脚本对其进行工具化
./bin/node --js.print --experimental-options --polyglot --insight=source-tracing.rb agent-fib.js
Ruby: Initializing GraalVM Insight script
Ruby: Hooks are ready!
Ruby: observed loading of node:internal/errors
Ruby: observed loading of node:internal/util
Ruby: observed loading of node:events
....
Ruby: observed loading of node:internal/modules/run_main
Ruby: observed loading of <...>/agent-fib.js
Three is the result 3
要跟踪变量值,请创建agent.rb脚本
insight.on("enter", -> (ctx, frame) {
puts("minusOne #{frame.n}")
}, {
roots: true,
rootNameFilter: "minusOne",
at: {
sourcePath: ".*agent-fib.js"
}
})
此代码使用了在 GraalVM 22.2 中引入的源位置声明性规范。使用较旧的 GraalVM 版本使用动态sourceFilter
insight.on("enter", -> (ctx, frame) {
puts("minusOne #{frame.n}")
}, {
roots: true,
rootNameFilter: "minusOne",
sourceFilter: -> (src) {
return src.name == Dir.pwd+"/agent-fib.js"
}
})
上面的 Ruby 脚本示例在agent-fib.js程序中调用minusOne
函数时,打印出变量n
的值
./bin/node --js.print --experimental-options --polyglot --insight=agent.rb agent-fib.js
minusOne 4
minusOne 3
minusOne 2
minusOne 2
Three is the result 3
R 洞察 #
可以使用 R 语言编写相同的工具。
创建agent-r.R脚本
cat("R: Initializing GraalVM Insight script\n")
insight@on('source', function(env) {
cat("R: observed loading of ", env$name, "\n")
})
cat("R: Hooks are ready!\n")
使用它来跟踪test.R程序
./bin/Rscript --insight=agent-r.R test.R
R: Initializing GraalVM Insight script
R: Hooks are ready!
R: observed loading of test.R
唯一的变化是 R 语言。所有其他 GraalVM 洞察功能和API保持不变。
C 代码洞察 #
不仅可以解释动态语言,而且借助GraalVM 的 LLI 实现,甚至可以将用C、C++、Fortran、Rust等编写的静态编译程序混合在一起。
例如,使用一个长时间运行的程序,如sieve.c,它在main
方法中包含一个永无止境的for
循环。您可能希望为它提供一些执行配额。
首先,在 GraalVM 上执行程序
export TOOLCHAIN_PATH=`.../bin/lli --print-toolchain-path`
${TOOLCHAIN_PATH}/clang agent-sieve.c -lm -o sieve
./bin/lli sieve
GraalVM 的clang
包装器添加了特殊的选项,指示常规的clang
将 LLVM 位码信息保存在sieve
可执行文件中,以及正常的本机代码。然后,GraalVM 的lli
解释器可以使用位码以全速解释程序。顺便说一下,比较直接通过./sieve
执行本机代码的结果和./bin/lli sieve
的解释器速度。对于解释器来说,它应该显示相当好的结果。
现在专注于打破无限循环。您可以使用此 JavaScript agent-limit.js洞察脚本做到这一点
var counter = 0;
insight.on('enter', function(ctx, frame) {
if (++counter === 1000) {
throw `GraalVM Insight: ${ctx.name} method called ${counter} times. enough!`;
}
}, {
roots: true,
rootNameFilter: 'nextNatural'
});
该脚本计算 C nextNatural
函数调用的次数,当该函数被调用一千次时,它会发出一个错误来停止sieve
的执行。以以下方式运行程序
./bin/lli --polyglot --insight=agent-limit.js sieve
Computed 97 primes in 181 ms. Last one is 509
GraalVM Insight: nextNatural method called 1000 times. enough!
at <js> :anonymous(<eval>:7:117-185)
at <llvm> nextNatural(agent-sieve.c:14:186-221)
at <llvm> nextPrime(agent-sieve.c:74:1409)
at <llvm> measure(agent-sieve.c:104:1955)
at <llvm> main(agent-sieve.c:123:2452)
可以从本机代码访问原始局部变量。将上面的洞察脚本替换为
insight.on('enter', function(ctx, frame) {
print(`found new prime number ${frame.n}`);
}, {
roots: true,
rootNameFilter: (n) => n === 'newFilter'
});
每次将新的素数添加到过滤器列表中时,打印一条消息
./bin/lli --polyglot --insight=agent-limit.js sieve | head -n 3
found new prime number 2
found new prime number 3
found new prime number 5
lli
、多语言和 GraalVM 洞察的混合为本机程序的跟踪、控制和交互式或批处理调试打开了巨大的可能性。
检查值 #
GraalVM 洞察不仅允许您跟踪程序执行发生的位置,而且还提供对执行期间局部变量和函数参数值的访问权限。例如,可以编写一个工具,它显示fib
函数中参数n
的值
insight.on('enter', function(ctx, frame) {
print('fib for ' + frame.n);
}, {
roots: true,
rootNameFilter: 'fib'
});
该工具使用第二个函数参数frame
来访问每个被工具化函数内部的局部变量的值。上面的洞察脚本还使用rootNameFilter
将它的钩子仅应用于名为fib
的函数
function fib(n) {
if (n < 1) return 0;
if (n < 2) return 1;
else return fib(n - 1) + fib(n - 2);
}
print("Two is the result " + fib(3));
当工具存储在fib-trace.js文件中,实际代码存储在fib.js中时,调用以下命令会产生有关程序执行和函数调用之间传递参数的详细信息
./bin/node --js.print --experimental-options --insight=fib-trace.js fib.js
fib for 3
fib for 2
fib for 1
fib for 0
fib for 1
Two is the result 2
为了总结本节,GraalVM 洞察是一个有用的工具,用于多语言、与语言无关的面向方面的编程。
修改局部变量 #
GraalVM 洞察不仅可以访问局部变量,还可以修改它们。例如,这个程序对数组求和
function plus(a, b) {
return a + b;
}
var sum = 0;
[1, 2, 3, 4, 5, 6, 7, 8, 9].forEach((n) => sum = plus(sum, n));
print(sum);
它打印出数字45
。应用以下洞察脚本,在添加非偶数之前“擦除”它们
insight.on('enter', function zeroNonEvenNumbers(ctx, frame) {
if (frame.b % 2 === 1) {
frame.b = 0;
}
}, {
roots: true,
rootNameFilter: 'plus'
});
当使用js --insight=erase.js sumarray.js
启动时,只会打印出值20
。
GraalVM 洞察的enter
和return
钩子只能修改现有变量。它们不能引入新的变量。尝试这样做会导致异常。
特定位置的洞察 #
要访问特定代码位置的变量,at
对象可能不仅具有一个必需的源规范:具有与源文件路径匹配的正则表达式的sourcePath
属性,或者具有源 URI 的字符串表示的sourceURI
属性。还可以指定可选的line
和/或column
。让我们有一个distance.js源文件
(function(x, y) {
let x2 = x*x;
let y2 = y*y;
let d = Math.sqrt(x2 + y2);
for (let i = 0; i < d; i++) {
// ...
}
return d;
})(3, 4);
然后,我们可以应用以下distance-trace.js洞察脚本来获取变量的值
insight.on('enter', function(ctx, frame) {
print("Squares: " + frame.x2 + ", " + frame.y2);
}, {
statements: true,
at: {
sourcePath: ".*distance.js",
line: 4
}
});
insight.on('enter', function(ctx, frame) {
print("Loop var i = " + frame.i);
}, {
expressions: true,
at: {
sourcePath: ".*distance.js",
line: 5,
column: 21
}
});
这给了我们
./bin/js --insight=distance-trace.js distance.js
Squares: 9, 16
Loop var i = 0
Loop var i = 1
Loop var i = 2
Loop var i = 3
Loop var i = 4
Loop var i = 5
延迟 Node.JS 中的洞察初始化 #
GraalVM 洞察可以在任何 GraalVM 语言运行时中使用,包括node
实现。但是,当在node
中时,您不想编写简单的洞察脚本。您可能希望利用node
生态系统的全部功能,包括它的模块。这是一个示例agent-require.js脚本,它可以做到这一点
let initialize = function (require) {
let http = require("http");
print(`${typeof http.createServer} http.createServer is available to the agent`);
}
let waitForRequire = function (event) {
if (typeof process === 'object' && process.mainModule && process.mainModule.require) {
insight.off('source', waitForRequire);
initialize(process.mainModule.require.bind(process.mainModule));
}
};
insight.on('source', waitForRequire, { roots: true });
Insight 脚本会在尽可能早的时候初始化,而此时 require
函数还没有准备好。因此,脚本首先在已加载的脚本上附加一个监听器,并在加载主用户脚本时,获取其 process.mainModule.require
函数。然后,它使用 insight.off
删除探针,并调用实际的 initialize
函数来执行真正的初始化,同时能够访问所有节点模块。脚本可以使用以下命令运行
./bin/node --js.print --experimental-options --insight=agent-require.js yourScript.js
此初始化序列已知在 GraalVM 的 node
版本 12.10.0 上运行,该版本使用主 yourScript.js
参数启动。
处理异常 #
GraalVM Insight 仪器可能会抛出异常,这些异常随后会传播到周围的用户脚本。假设你有一个程序 seq.js 用于记录各种消息
function log(msg) {
print(msg);
}
log('Hello GraalVM Insight!');
log('How');
log('are');
log('You?');
你可以注册一个仪器 term.js 并根据观察到的日志消息,在 seq.js 程序的中间停止执行
insight.on('enter', (ev, frame) => {
if (frame.msg === 'are') {
throw 'great you are!';
}
}, {
roots: true,
rootNameFilter: 'log'
});
term.js 仪器等待对 log
函数的调用,其消息为 are
,此时,它发出自己的异常,有效地中断用户程序的执行。结果,你将获得
./bin/js --polyglot --insight=term.js seq.js
Hello GraalVM Insight!
How
great you are!
at <js> :=>(term.js:3:75-97)
at <js> log(seq.js:1-3:18-36)
at <js> :program(seq.js:7:74-83)
Insight 仪器发出的异常被视为常规语言异常。seq.js 程序可以使用常规的 try { ... } catch (e) { ... }
块来捕获它们并像处理常规用户代码发出的异常一样处理它们。
拦截和修改执行 #
GraalVM Insight 能够修改程序的执行。它可以跳过某些计算,并用自己的替代方案替换它们。以下 plus
函数就是一个例子
function plus(a, b) {
return a + b;
}
很容易改变 plus
方法的行为。以下 Insight 脚本使用 ctx.returnNow
功能将 +
操作替换为乘法
insight.on('enter', function(ctx, frame) {
ctx.returnNow(frame.a * frame.b);
}, {
roots: true,
rootNameFilter: 'plus'
});
returnNow
方法立即停止执行并返回到 plus
函数的调用者。plus
方法的正文根本不会执行,因为在实际执行函数正文之前,例如,应用了 insight on('enter', ...)
。将两个数字相乘而不是相加可能听起来不太诱人,但同样的方法对于提供重复函数调用的附加缓存(例如,记忆化)很有用。
也可以让原始函数代码运行,只是修改其结果。例如,修改 plus
函数的结果,使其始终非负
insight.on('return', function(ctx, frame) {
let result = ctx.returnValue(frame);
ctx.returnNow(Math.abs(result));
}, {
roots: true,
rootNameFilter: 'plus'
});
Insight 挂钩在 plus
函数返回时执行,并使用 returnValue
辅助函数从当前 frame
对象获取计算的返回值。然后,它可以修改该值,并且 returnNow
返回一个新的结果。returnValue
函数始终在提供的 ctx
对象上可用,但只有在 on('return', ...)
挂钩中使用时,才会返回有意义的值。
最小开销 #
如果你问 GraalVM Insight 在应用脚本时是否会造成任何性能开销,答案是“否”或“最小”。开销取决于你的脚本做了什么。如果它们在整个代码库中添加并传播复杂的计算,那么计算的代价将由你来支付。但是,这将是你的代码的开销,而不是仪器的开销。使用简单的 function-count.js 脚本,可以衡量开销。
var count = 0;
function dumpCount() {
print(`${count} functions have been executed`);
}
insight.on('enter', function(ev) {
count++;
}, {
roots: true
});
insight.on('close', dumpCount);
将脚本用于 sieve.js 示例的五十次迭代,该示例使用埃拉托斯特尼筛法的变体来计算十万个素数。重复计算五十次,可以让运行时有机会热身并进行适当的优化。以下是最佳运行
./bin/js sieve.js | grep -v Computed
Hundred thousand prime numbers in 75 ms
Hundred thousand prime numbers in 73 ms
Hundred thousand prime numbers in 73 ms
现在,将其与启用 GraalVM Insight 脚本时的执行时间进行比较
./bin/js --insight=function-count.js sieve.js | grep -v Computed
Hundred thousand prime numbers in 74 ms
Hundred thousand prime numbers in 74 ms
Hundred thousand prime numbers in 75 ms
72784921 functions have been executed
差异为 2 毫秒。GraalVM Insight 将程序代码和 insight 收集脚本之间的差异融合在一起,使所有代码都作为一个整体工作。count++
调用成为程序的自然组成部分,在所有代表程序函数的 ROOT
的位置。
访问局部变量时的最小开销 #
GraalVM Insight 能够几乎“免费”访问局部变量。访问局部变量的 GraalVM Insight 代码与定义它们的实际函数代码融合在一起,没有明显的减速。
这可以通过使用以下 sieve.js 算法来计算十万个素数来演示。它将找到的素数保存在一个通过以下函数构造的链表中
function Filter(number) {
this.number = number;
this.next = null;
this.last = this;
}
首先,通过调用计算五十次并测量完成最后一轮所需的时间来测试其行为
./bin/js -e "var count=50" --file sieve.js | grep Hundred | tail -n 1
Hundred thousand prime numbers in 73 ms
然后,通过观察每次分配新的素数槽,例如,调用 new Filter
构造函数,来“戏弄”系统
var sum = 0;
var max = 0;
insight.on('enter', (ctx, frame) => {
sum += frame.number;
if (frame.number > max) {
max = frame.number;
}
}, {
roots: true,
rootNameFilter: 'Filter'
});
insight.on('return', (ctx, frame) => {
log(`Hundred thousand prime numbers from 2 to ${max} has sum ${sum}`);
sum = 0;
max = 0;
}, {
roots: true,
rootNameFilter: 'measure'
});
每次分配 new Filter(number)
时,都会捕获 number
的最大值(例如,找到的最高素数),以及到目前为止找到的所有素数的 sum
。当 measure
中的主循环结束时(意味着已找到十万个素数),结果将被打印出来。
现在,尝试以下操作
./bin/js -e "var count=50" --insight=sieve-filter1.js --file sieve.js | grep Hundred | tail -n 2
Hundred thousand prime numbers from 2 to 1299709 has sum 62260698721
Hundred thousand prime numbers in 74 ms
根本没有减速。GraalVM Insight 与 GraalVM 编译器的内联算法相结合,能够以几乎没有性能损失的代价实现强大的仪器功能。
访问执行堆栈 #
GraalVM Insight 有一种方法可以访问整个执行堆栈。以下代码片段展示了如何做到这一点
insight.on("return", function(ctx, frame) {
print("dumping locals");
ctx.iterateFrames((at, vars) => {
for (let p in vars) {
print(` at ${at.name} (${at.source.name}:${at.line}:${at.column}) ${p} has value ${vars[p]}`);
}
});
print("end of locals");
}, {
roots: true
});
每当触发 Insight 挂钩时,它都会打印当前的执行堆栈,包括函数的 name
、source.name
、line
和 column
。此外,它还会打印每个框架中所有局部 vars
的值。也可以通过为它们分配新值来修改现有变量的值:vars.n = 42
。访问整个堆栈很灵活,但与 访问当前执行框架中的局部变量 不同,这不是一项快速操作,如果希望程序继续以全速运行,请明智地使用它。
堆转储 #
GraalVM Insight 可用于在执行期间快照程序堆的某个区域。将 --heap.dump=/path/to/output.hprof
选项与常规的 --insight
选项一起使用。Insight 脚本将获取具有 dump
函数的 heap
对象的访问权限。将你的挂钩放在你需要的地方,并在合适的时候转储堆
insight.on('return', (ctx, frame) => {
heap.dump({
format: '1.0',
depth: 50, // set max depth for traversing object references
events: [
{
stack : [
{
at : ctx, // location of dump sieve.js:73
frame : {
// assemble frame content as you want
primes : frame.primes, // capture primes object
cnt : frame.cnt, // capture cnt value
},
depth : 10 // optionally override depth to ten references
}, // there can be more stack elements like this one
]
},
// there can be multiple events like the previous one
],
});
throw 'Heap dump written!';
}, {
roots: true,
rootNameFilter: 'measure'
});
将代码片段保存为 dump.js 文件。获取 sieve.js 文件,并将其启动为
./bin/js --insight=dump.js --heap.dump=dump.hprof --file sieve.js
measure
函数结束时将创建一个 dump.hprof 文件,用于捕获程序内存的状态。使用常规工具(如 VisualVM 或 NetBeans)检查生成的 .hprof 文件
上图显示了在 sieve.js 脚本中 measure
函数结束时拍摄的堆转储。该函数刚刚计算了十万个(在变量 cnt
中可用)素数。该图显示了一个链表 Filter
,它保存了从 2
到 17
的素数。链表的其余部分隐藏在 unreachable
对象后面(只请求了深度为 10
的引用)。最后一个变量 x
显示了为了计算所有素数而搜索的自然数的数量。
堆转储缓存 #
为了加快堆转储过程并优化最终的转储,可以启用内存缓存。在转储到缓存之间没有更改其属性的对象只存储一次,从而减小最终的堆转储大小。添加(例如)--heap.cacheSize=1000
选项以对 1000 个事件使用内存缓存。默认情况下,缓存被转储到文件中,并在填满时被清除。可以通过 --heap.cacheReplacement=lru
选项更改此策略,该选项在缓存大小限制达到时,将最近的转储事件保留在缓存中,并删除最旧的事件。
要将缓存刷新到堆转储文件,需要显式调用 heap.flush()
。
关于 GraalVM Insight API 的说明 #
通过 insight
对象公开的 GraalVM Insight API 的兼容性以兼容的方式实现。GraalVM Insight API 可以通过 此链接 找到。insight
对象的属性和函数作为其 javadoc 的一部分提供。
未来的版本将添加新功能,但无论以前公开过什么功能,它都将保持有效。如果你的脚本依赖于某些新功能,它可能会检查公开的 API 的版本
print(`GraalVM Insight version is ${insight.version}`);
API 中的新元素带有关联的 @since
标记,用于描述关联功能自何版本起可用。