28、Nashorn脚本的调试、跟踪与性能分析

Nashorn脚本的调试、跟踪与性能分析

1. 调试独立脚本

1.1 运行和调试准备

要在NetBeans中运行或调试独立的Nashorn脚本,首先需要在NetBeans IDE中打开脚本文件。打开脚本后,有两种方式运行脚本:
- 右键点击显示脚本的编辑器,选择“Run File”菜单项。
- 在脚本面板处于活动状态时,按“Shift + F6”。

1.2 设置断点

可以使用以下三种方法之一添加断点:
- 将光标放在要设置/取消断点的行,右键点击并选择“Toggle Line Breakpoint”菜单项来设置或取消断点。
- 将光标放在要设置/取消断点的行,按“Ctrl + F8”。首次按下此组合键设置断点,若断点已设置,再次按下则取消断点。
- 在脚本中添加 debugger 语句。当调试会话处于活动状态时, debugger 语句的作用就像一个断点;否则,它没有任何效果。

1.3 开始调试

设置好断点后,有两种方式开始调试脚本:
- 右键点击脚本面板,选择“Debug File”菜单项。
- 在脚本面板处于活动状态时,按“Ctrl + Shift + F5”。

调试会话开始后,调试器会在第一个断点处停止。此时,底部会打开“Variables”面板,显示作用域内所有变量及其值。若要查看调试会话的其他详细信息,可使用主菜单“Window ➤ Debugging”下的菜单项。若关闭了某些调试面板,可使用“Window ➤ Debugging”菜单重新打开。

1.4 调试操作

当调试会话处于活动状态时,可以使用以下调试操作,这些操作可从主菜单“Debug”以及调试器工具栏获取:
| 调试操作 | 快捷键 | 描述 |
| — | — | — |
| Finish Debugger | Shift + F5 | 结束调试会话。 |
| Pause | 无 | 停止当前会话中的所有线程。 |
| Continue | Ctrl + F5 | 恢复程序执行,直到下一个断点。 |
| Step Over | F8 | 执行当前行,并将程序计数器移动到文件中的下一行。如果执行的行是对函数的调用,函数中的代码也会被执行。 |
| Step Over Expression | Shift + F8 | 允许你逐步执行表达式中的每个方法调用,并查看每个方法调用的输入参数和结果输出值。如果没有更多的方法调用,其行为与“Step Over”操作相同。 |
| Step Into | F7 | 执行当前行。如果该行是对函数的调用,并且有被调用函数的源代码可用,程序计数器将移动到函数的声明处;否则,程序计数器将移动到脚本中的下一行。 |
| Step Out | Ctrl + F7 | 执行当前函数中剩余的代码,并将程序计数器移动到调用该函数的行之后。如果你已经进入了一个不需要再调试的函数,可以使用此操作。 |

1.5 示例代码

以下是一个简单的判断质数的脚本示例:

// Check if n is divisible by any odd integers between 3 and sqrt(n).
function isPrime(n) {
    var sqrt = Math.sqrt(n);
    for (var i = 3; i <= sqrt; i += 2) {
        if (n % i === 0) {
            return false;
        }
    }
    return true; // If we get here, it is a prime number.
}

// Check few nubmers for being primes
var num = 8;
var isPrimeNum = isPrime(num);
print(num + " is a prime number: " + isPrimeNum);

debugger;
num = 37;
isPrimeNum = isPrime(num);
print(num + " is a prime number: " + isPrimeNum);

2. 从Java代码中调试脚本

2.1 调试步骤

从Java代码调用脚本进行调试的方式略有不同。仅在脚本文件中设置断点并启动调试器,或从Java代码进入脚本进行调试是行不通的。以下是具体的调试步骤:
1. 设置断点 :需要在调用脚本引擎 eval() 方法的代码行设置断点。例如,在以下Java程序中,需要在调用 engine.eval(scriptReader); 的行设置断点:

// PrimeTest.java
package com.jdojo.script;

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class PrimeTest {
    public static void main(String[] args) {
        // Construct the script file path
        String scriptFileName = "primetest.js";
        Path scriptPath = Paths.get(scriptFileName);

        // Make sure the script file exists. If not, print the full
        // path of the script file and terminate the program.
        if (!Files.exists(scriptPath)) {
            System.out.println(scriptPath.toAbsolutePath() + " does not exist.");
            return;
        }

        // Get the Nashorn script engine
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        try {
            // Get a Reader for the script file
            Reader scriptReader = Files.newBufferedReader(scriptPath);

            // Execute the script in the file
            engine.eval(scriptReader);  // First time, add a breakpoint here
        }
        catch (IOException | ScriptException e) {
            e.printStackTrace();
        }
    }
}
  1. 启动调试 :在包含 PrimeTest 类的编辑器面板处于活动状态时,按“Ctrl + Shift + F5”。调试器将在第35行的断点处停止。
  2. 进入脚本 :按“F7”进入 eval() 方法调用,这将带你到 AbstractScriptEngine.java 文件。再次按“F7”,调试器将打开一个名为 <eval>.js 的文件,其中包含你试图通过Java代码使用 Reader 加载的 primetest.js 文件中的脚本。你可以滚动查看脚本并在 <eval>.js 文件中设置断点。
  3. 继续调试 :在 <eval>.js 文件中设置好断点后,就可以进行正常的调试操作了。例如,使用“Continue”调试操作(F5)将在执行到下一个断点时停止。

2.2 注意事项

调试完成后,可以从Java代码(如本例中的 PrimeTest.java 文件)中移除断点。如果开始新的调试会话,调试器将在之前在 <eval>.js 文件中设置的断点处停止。需要注意的是,只需进入 <eval>.js 文件一次,后续的调试会话会记住之前会话设置的断点。

2.3 代码替换

在上述Java程序中, try-catch 块中的代码可以替换为以下代码片段,程序的功能相同,但需要移除两个从 java.io 包导入类的导入语句:

try {
    // Execute the script in the file
    engine.eval("load('" + scriptFileName + "');"); // First time, add a breakpoint here
}
catch (ScriptException e) {
    e.printStackTrace();
}

2.4 调试流程

graph TD;
    A[设置eval()方法调用处的断点] --> B[启动调试器];
    B --> C[调试器在断点处停止];
    C --> D[按F7进入eval()方法];
    D --> E[打开<eval>.js文件];
    E --> F[在<eval>.js文件中设置断点];
    F --> G[进行正常调试操作];

3. 脚本的跟踪与性能分析

3.1 启用选项

Nashorn支持调用点跟踪和性能分析。可以在 jjs 命令行工具以及嵌入式Nashorn引擎中启用这些选项。可以为引擎运行的所有脚本或每个脚本/函数启用跟踪和性能分析:
- –tcs 选项:为所有脚本启用调用点跟踪,并将调用点跟踪信息打印到标准输出。
- -pcs 选项:为所有脚本启用调用点性能分析,并将调用点性能分析数据打印到当前目录下名为 NashornProfile.txt 的文件中。

3.2 选择性跟踪和分析

可以在脚本或函数的开头使用以下四个Nashorn指令来选择性地跟踪和分析整个脚本或函数:
- "nashorn callsite trace enterexit" :相当于 -tcs=enterexit
- "nashorn callsite trace miss" :相当于 -tcs=miss
- "nashorn callsite trace objects" :相当于 -tcs=objects
- "nashorn callsite profile" :相当于 -pcs

需要注意的是, –tcs –pcs 选项是基于每个脚本引擎的,而这四个跟踪和分析指令是基于每个脚本和每个函数的。这些Nashorn指令仅在调试模式下启用,可以通过将 nashorn.debug 系统属性设置为 true 来启用Nashorn调试模式。这些指令在JDK8u40及更高版本中可用。

3.3 示例代码

以下是一个启用了Nashorn调用点性能分析选项的脚本示例,该脚本保存为 primeprofiler.js 文件:

// primeprofiler.js

function isPrime(n) {
    // Profile this function only
    "nashorn callsite profile";

    // Integers <= 2, floating-point numbers, and even numbers are not primes
    if (n <= 2 || Math.floor(n) !== n || n % 2 === 0) {
        return false;
    }

    // Check if n is divisible by any odd integers between 3 and sqrt(n).
    var sqrt = Math.sqrt(n);
    for (var i = 3; i <= sqrt; i += 2) {
        if (n % i === 0) {
            return false;
        }
    }
    return true; // If we get here, it is a prime number.
}

// Check few nubmers for being primes
var num = 8;
var isPrimeNum = isPrime(num);
print(num + " is a prime number: " + isPrimeNum);

num = 37;
isPrimeNum = isPrime(num);
print(num + " is a prime number: " + isPrimeNum);

3.4 运行脚本并生成分析文件

使用以下命令以启用Nashorn调试选项的方式运行 primeprofile.js 文件中的脚本:

c:\>jjs -J-Dnashorn.debug=true primeprofile.js

运行该命令后,将在当前目录下生成一个名为 NashornProfile.txt 的文件,其中包含 isPrime() 函数调用的性能分析数据。以下是 NashornProfile.txt 文件的内容示例:

0        dyn:getProp|getElem|getMethod:Math        438462        2
1        dyn:getMethod|getProp|getElem:floor       433936        2
2        dyn:call                                  650602        2
3        dyn:getProp|getElem|getMethod:Math        313834        1
4        dyn:getMethod|getProp|getElem:sqrt        283356        1
5        dyn:call                                       0        1

3.5 Java程序示例

以下是一个设置 nashorn.debug 系统属性并运行上述脚本的Java程序:

// ProfilerTest.java
package com.jdojo.script;

import java.io.IOException;
import java.io.Reader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ProfilerTest {
    public static void main(String[] args) {
        // Set the nashorn.debug system property, so the tracing and
        // profiling directives will be recognized
        System.setProperty("nashorn.debug", "true");

        // Construct the script file path
        String scriptFileName = "primeprofiler.js";
        Path scriptPath = Paths.get(scriptFileName);

        // Make sure the script file exists. If not, print the full
        // path of the script file and terminate the program.
        if (!Files.exists(scriptPath)) {
            System.out.println(scriptPath.toAbsolutePath() + " does not exist.");
            return;
        }

        // Get the Nashorn script engine
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        try {
            // Get a Reader for the script file
            Reader scriptReader = Files.newBufferedReader(scriptPath);

            // Execute the script in the file
            engine.eval(scriptReader);
        }
        catch (IOException | ScriptException e) {
            e.printStackTrace();
        }
    }
}

运行该程序将在当前目录下创建一个 NashornProfile.txt 文件,文件内容与上述示例相同。

3.6 跟踪和分析流程

graph TD;
    A[设置nashorn.debug系统属性为true] --> B[编写脚本并添加跟踪或分析指令];
    B --> C[运行脚本(Java程序或jjs命令)];
    C --> D[生成NashornProfile.txt文件];
    D --> E[查看性能分析数据];

综上所述,NetBeans 8搭配JDK 8或更高版本支持在NetBeans IDE中调试Nashorn脚本,既可以运行和调试独立的Nashorn脚本,也可以在从Java代码调用Nashorn脚本时进行调试,调试器能够无缝地从Java代码跳转到Nashorn脚本。此外,Nashorn支持调用点跟踪和性能分析,可以通过命令行工具或嵌入式引擎启用这些选项,还可以使用特定的指令对脚本或函数进行选择性的跟踪和分析。

4. 调试和分析的实际应用场景

4.1 调试场景

在实际开发中,调试是发现和解决代码问题的重要手段。以下是一些常见的调试场景:
- 功能异常 :当脚本的功能没有按预期执行时,例如判断质数的脚本返回了错误的结果,可以通过设置断点,逐步执行代码,查看变量的值,找出问题所在。
- 性能问题 :如果脚本执行速度过慢,可以使用调试工具分析代码的执行流程,找出性能瓶颈,例如某个循环或函数调用消耗了过多的时间。
- 兼容性问题 :在不同的环境或版本中,脚本可能会出现兼容性问题。通过调试可以在不同的环境中运行脚本,观察变量和执行结果的差异,找出问题的根源。

4.2 跟踪和分析场景

跟踪和分析可以帮助开发者了解脚本的执行情况,优化代码性能。以下是一些常见的跟踪和分析场景:
- 性能优化 :通过分析调用点性能分析数据,可以找出哪些函数或方法调用频繁,消耗了较多的资源,从而对这些部分进行优化。
- 代码优化 :跟踪调用点信息可以帮助开发者了解代码的执行路径,发现不必要的函数调用或重复的代码,进行代码重构。
- 问题定位 :当脚本出现问题时,调用点跟踪信息可以提供更多的上下文,帮助开发者快速定位问题所在。

4.3 实际应用示例

假设我们有一个复杂的脚本,其中包含多个函数调用和循环。在开发过程中,发现脚本执行速度较慢,我们可以使用跟踪和分析功能来找出问题所在:
1. 在脚本开头添加 "nashorn callsite profile" 指令,启用调用点性能分析。
2. 运行脚本,生成 NashornProfile.txt 文件。
3. 分析 NashornProfile.txt 文件中的数据,找出调用次数较多或执行时间较长的函数。
4. 对这些函数进行优化,例如减少不必要的循环或函数调用。
5. 再次运行脚本,查看性能是否有所提升。

5. 总结与建议

5.1 总结

通过本文的介绍,我们了解了Nashorn脚本的调试、跟踪和性能分析的相关知识和操作方法:
- 调试 :可以在NetBeans IDE中调试独立的Nashorn脚本,也可以从Java代码中调试脚本。通过设置断点、使用调试操作(如Step Over、Step Into等),可以逐步执行代码,查看变量的值,找出问题所在。
- 跟踪和分析 :Nashorn支持调用点跟踪和性能分析,可以通过 –tcs -pcs 选项为所有脚本启用,也可以使用Nashorn指令为特定的脚本或函数启用。跟踪和分析数据可以帮助我们了解脚本的执行情况,优化代码性能。

5.2 建议

在实际开发中,为了更好地利用调试、跟踪和分析功能,提高开发效率和代码质量,我们可以遵循以下建议:
- 养成调试习惯 :在开发过程中,遇到问题时及时使用调试工具进行排查,不要盲目猜测和修改代码。
- 合理使用跟踪和分析 :根据实际需求,选择合适的跟踪和分析选项,避免过度使用导致性能下降。
- 定期优化代码 :根据跟踪和分析结果,定期对代码进行优化,提高代码的性能和可维护性。

5.3 未来展望

随着技术的不断发展,Nashorn脚本的调试、跟踪和分析功能可能会不断完善和增强。例如,可能会提供更直观的调试界面、更详细的跟踪和分析数据,以及更智能的问题定位和优化建议。开发者可以关注这些发展趋势,不断提升自己的开发能力和效率。

5.4 操作总结表格

操作类型 具体操作 相关选项/指令 作用
调试 在NetBeans中打开脚本文件,设置断点,启动调试 找出代码中的问题
调试 从Java代码中调试脚本,设置 eval() 方法调用处的断点,进入脚本调试 调试从Java代码调用的脚本
跟踪和分析 jjs 命令行工具或嵌入式引擎中使用 –tcs -pcs 选项 –tcs -pcs 为所有脚本启用调用点跟踪和性能分析
跟踪和分析 在脚本或函数开头使用Nashorn指令 "nashorn callsite trace enterexit" "nashorn callsite trace miss" "nashorn callsite trace objects" "nashorn callsite profile" 选择性地跟踪和分析脚本或函数

5.5 综合流程图

graph LR;
    A[开发脚本] --> B{出现问题?};
    B -- 是 --> C[使用调试工具排查];
    C --> D[设置断点,逐步执行代码];
    D --> E[查看变量值,找出问题];
    B -- 否 --> F{需要优化性能?};
    F -- 是 --> G[使用跟踪和分析功能];
    G --> H[添加跟踪或分析指令];
    H --> I[运行脚本,生成分析文件];
    I --> J[分析数据,找出性能瓶颈];
    J --> K[优化代码];
    F -- 否 --> L[继续开发];
    E --> M[修复问题,继续开发];
    K --> N[再次运行脚本,验证性能提升];
    N --> L;

通过以上的调试、跟踪和分析方法,开发者可以更高效地开发和优化Nashorn脚本,提高代码的质量和性能。希望本文的内容对开发者有所帮助,让大家在开发过程中能够更加得心应手。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值