Nashorn 达到了绑定值的某种限制

问题描述 投票:0回答:1

我正在使用 openjdk 版本的 Nashorn 15.4,使用 Java 21Java 11 进行测试,结果相同。

我的代码加载 Nashorn 脚本引擎绑定表示元数据字段的值,并允许用户编写 Javascript 来修改这些元数据字段,方法是将脚本传递给 eval 方法,然后检查修改后的元数据值。它工作正常,但随着我增加了添加到绑定的值的数量,我遇到了意想不到的问题,尽管脚本没有修改这些值,但某些绑定变量值丢失或更改。

提供一些背景信息的屏幕截图

enter image description here

我现在可以用独立的示例来模拟问题

import javax.script.ScriptContext;
import javax.script.ScriptEngine;

public class JsTest
{
     public static void main(String[] args) throws Exception
     {
         int numberOfBindingVars = Integer.parseInt(args[0]);
         ScriptEngine engine;
         for(int i=0; i<10; i++)
         {
             engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine();
             for(int k=0;k<numberOfBindingVars;k++)
             {
                 engine.put("var"+k,"1963");
             }

             System.out.println(String.format("Bindings %d", engine.getBindings(ScriptContext.ENGINE_SCOPE).size()));
             for(int k=0;k<numberOfBindingVars;k++)
             {
                 if(engine.get("var"+k)==null)
                 {
                     System.out.println("Missing var"+k);
                 }
             }
         }
     }
}

使用参数 400 运行它效果很好,并给了我

Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400

但是,如果我将参数增加到500,则绑定计数不一致,尽管奇怪地检查每个值并没有发现任何缺失。

Bindings 500
Bindings 499
Bindings 498
Bindings 497
Bindings 496
Bindings 495
Bindings 494
Bindings 500
Bindings 499
Bindings 498

如果使用 1000 运行,我会再次得到不一致的计数,也无法在 500 标记附近找到某些变量

Bindings 1000
Bindings 999
Missing var448
Bindings 1000
Bindings 999
Missing var448
Bindings 998
Missing var448
Missing var449
Bindings 997
Missing var448
Missing var449
Missing var450
Bindings 996
Missing var448
Missing var449
Missing var450
Missing var451
Bindings 995
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Bindings 994
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Missing var453
Bindings 993
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Missing var453
Missing var454

在所有情况下,第一次都可以正常工作,但随着 500 次和 1000 次调用,后续迭代就不正确。

更多测试对于 448 个绑定总是没问题,但对于 449 或更大的绑定则失败。

似乎有其他一些 Nashorn 进程在修剪 Bindings 的大小,这肯定是一个错误?

希望获得一些对 nashorn 更深入了解的意见

javascript java nashorn
1个回答
0
投票

Java

$ java -version
openjdk version "17.0.12" 2024-07-16
OpenJDK Runtime Environment Temurin-17.0.12+7 (build 17.0.12+7)
OpenJDK 64-Bit Server VM Temurin-17.0.12+7 (build 17.0.12+7, mixed mode, sharing)

项目树

jsEngineScript
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── example
                    └── JsTest.java

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>js-engine-script</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>jsEngineScript</name>
    <description>jsEngineScript App Project</description>
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjdk.nashorn</groupId>
            <artifactId>nashorn-core</artifactId>
            <version>15.4</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>app</finalName>
    </build>

</project>

JsTest.java

  • static void forceGC()
    :执行垃圾收集
  • showJVMMem()
    :显示JVM内存设置
package com.example;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;

public class JsTest {
    public static void main(String[] args) throws Exception {
        boolean enableForceGC = false;

        //default for loop = 10;
        int inputForLoop = 10;

        if (args.length >= 2) {
            for (int x = 1; x < args.length; x++) {
                if (args[x].equals("--enableForceGC")) {
                    enableForceGC = true;
                }
                if (args[x].startsWith("--inputFor=")) {
                    String inputForNum = args[x].substring("--inputFor=".length());
                    //System.out.println("inputForNum = " + inputForNum);
                    inputForLoop = Integer.parseInt(inputForNum);
                }
            }
        }

        showJVMMem();

        int numberOfBindingVars = Integer.parseInt(args[0]);
        System.out.println(">>>> numberOfBindingVars = " + numberOfBindingVars);
        System.out.println(">>>> inputForLoop = " + inputForLoop);
        System.out.println(">>>> enableForceGC = " + enableForceGC);
        System.out.println();

        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine;
        //for(int i=0; i<10; i++)
        for (int i = 0; i < inputForLoop; i++) {
            System.out.println(">>>> i = " + i);
            //engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine();
            engine = manager.getEngineByName("JavaScript");

            int kct = 0;
            for (int k = 0; k < numberOfBindingVars; k++) {
                engine.put("var" + k, "1963");
                kct = kct + 1;
            } //for k1

            System.out.println("i = " + i + "\tk count=" + kct);

            System.out.printf("i = " + i + "\tBindings %d%n", engine.getBindings(ScriptContext.ENGINE_SCOPE).size());

            // Traverse Bindings and output all variable names and corresponding values
            /*
            Bindings myBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
            System.out.println("i = " + i + "\tbindings size = " + myBindings.size());
            System.out.println("i = " + i +"\tBindings List:");
            System.out.println();

            for (String key : myBindings.keySet()) {
                System.out.println(key + " = " + myBindings.get(key));
            }
            */

            for (int k = 0; k < numberOfBindingVars; k++) {
                if (engine.get("var" + k) == null) {
                    System.out.println("Missing var" + k);
                }
            } //for k2

            engine = null;

            if (enableForceGC) forceGC();

            System.out.println(">>>>END i = " + i);
            System.out.println();
        } //for i

    } //main

    static void forceGC() {
        System.out.println();
        System.out.println(">>>> run forceGC()");
        System.gc(); // Or use Runtime.getRuntime().gc();

        // Wait a while to allow garbage collection to complete
        try {
            Thread.sleep(1000); // Pause 1 second
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    static void showJVMMem() {
        // Get a Runtime instance
        Runtime runtime = Runtime.getRuntime();

        // Output the total memory of the JVM
        long totalMemory = runtime.totalMemory();
        // Output the maximum memory of the JVM
        long maxMemory = runtime.maxMemory();
        // Output the used memory of the JVM
        long usedMemory = totalMemory - runtime.freeMemory();

        // Get MemoryMXBean
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();

        // Get heap memory usage
        MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage();

        // Output memory usage information
        System.out.println(">>>> Heap Memory Usage:");
        System.out.println("Initial Memory (Xms): " + formatSize(heapMemoryUsage.getInit()));
        System.out.println("Used Memory: " + formatSize(heapMemoryUsage.getUsed()));
        System.out.println("Max Memory (Xmx): " + formatSize(heapMemoryUsage.getMax()));
        System.out.println();
        System.out.println(">>>> JVM Memory Usage:");
        System.out.println("Total Memory: " + formatSize(totalMemory));
        System.out.println("Used Memory: " + formatSize(usedMemory));
        System.out.println("Free Memory: " + formatSize(runtime.freeMemory()));
        System.out.println("Max Memory: " + formatSize(maxMemory));
        System.out.println();
    }

    static String formatSize(long size) {
        String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
        int unitIndex = 0;
        double sizeInUnit = size;

        while (sizeInUnit >= 1024 && unitIndex < units.length - 1) {
            sizeInUnit /= 1024;
            unitIndex++;
        }

        return String.format("%.2f %s", sizeInUnit, units[unitIndex]);
    }

} //class

构建

构建

mvn clean package

下载库

mvn dependency:copy-dependencies -DoutputDirectory=target/libs

测试

测试1

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 1000 \
  >> result_for_10_Binding_1K_NoGC.txt

测试2

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 10000 \
  >> result_for_10_Binding_10K_NoGC.txt

测试3

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 100000 \
  >> result_for_10_Binding_100K_NoGC.txt

测试4

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 1000 \
  --enableForceGC \
  >> result_for_10_Binding_1K_ForceGC.txt

测试5

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 10000 \
  --enableForceGC \
  >> result_for_10_Binding_10K_ForceGC.txt

测试6

java -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 100000 \
  --enableForceGC \
  >> result_for_10_Binding_100K_ForceGC.txt

结论

我先给出结论:

  • (a) 在for循环中添加强制垃圾回收可以避免该问题。

  • (b) 如果不添加强制垃圾回收,给定不同的参数值,将会有不同的结果。当测试 paramter = 1000 (1K ), paramter = 10000 (10K), paramter = 100000 (100K) 时会有不同的结果。

测试结果:添加强制垃圾回收

  • 测试4:result_for_10_Binding_1K_ForceGC.txt
  • 测试5:result_for_10_Binding_10K_ForceGC.txt
  • 测试6:result_for_10_Binding_100K_ForceGC.txt

result_for_10_Binding_100K_ForceGC.txt

您可以打开txt文件并搜索

Missing
。如果没有出现
Missing
,说明可以使用gc来解决问题。

>>>> Heap Memory Usage:
Initial Memory (Xms): 500.00 MB
Used Memory: 0.00 B
Max Memory (Xmx): 7.81 GB

>>>> JVM Memory Usage:
Total Memory: 508.00 MB
Used Memory: 4.88 MB
Free Memory: 501.68 MB
Max Memory: 7.81 GB

>>>> numberOfBindingVars = 100000
>>>> inputForLoop = 10
>>>> enableForceGC = true

>>>> i = 0
i = 0   k count=100000
i = 0   Bindings 100000

>>>> run forceGC()
>>>>END i = 0

>>>> i = 1
i = 1   k count=100000
i = 1   Bindings 100000

>>>> run forceGC()
>>>>END i = 1

>>>> i = 2
i = 2   k count=100000
i = 2   Bindings 100000

>>>> run forceGC()
>>>>END i = 2

>>>> i = 3
i = 3   k count=100000
i = 3   Bindings 100000

>>>> run forceGC()
>>>>END i = 3

>>>> i = 4
i = 4   k count=100000
i = 4   Bindings 100000

>>>> run forceGC()
>>>>END i = 4

>>>> i = 5
i = 5   k count=100000
i = 5   Bindings 100000

>>>> run forceGC()
>>>>END i = 5

>>>> i = 6
i = 6   k count=100000
i = 6   Bindings 100000

>>>> run forceGC()
>>>>END i = 6

>>>> i = 7
i = 7   k count=100000
i = 7   Bindings 100000

>>>> run forceGC()
>>>>END i = 7

>>>> i = 8
i = 8   k count=100000
i = 8   Bindings 100000

>>>> run forceGC()
>>>>END i = 8

>>>> i = 9
i = 9   k count=100000
i = 9   Bindings 100000

>>>> run forceGC()
>>>>END i = 9

测试结果:默认,不强制垃圾回收

  • 测试1:result_for_10_Binding_1K_NoGC.txt
  • 测试2:result_for_10_Binding_10K_NoGC.txt
  • 测试3:result_for_10_Binding_100K_NoGC.txt

我必须告诉你,有趣的结果发生了。

测试1:result_for_10_Binding_1K_NoGC.txt

发现

Missing
:29次,当for循环=10时,输入参数为1000(1K)。

测试2:result_for_10_Binding_10K_NoGC.txt

发现

Missing
:1次,for循环=10时,输入参数为10000(10K)。

测试3:result_for_10_Binding_100K_NoGC.txt

发现

Missing
:0次,当for循环=10时,输入参数为100000(100K)。

>>>> Heap Memory Usage:
Initial Memory (Xms): 500.00 MB
Used Memory: 0.00 B
Max Memory (Xmx): 7.81 GB

>>>> JVM Memory Usage:
Total Memory: 508.00 MB
Used Memory: 4.88 MB
Free Memory: 501.68 MB
Max Memory: 7.81 GB

>>>> numberOfBindingVars = 100000
>>>> inputForLoop = 10
>>>> enableForceGC = false

>>>> i = 0
i = 0   k count=100000
i = 0   Bindings 100000
>>>>END i = 0

>>>> i = 1
i = 1   k count=100000
i = 1   Bindings 100000
>>>>END i = 1

>>>> i = 2
i = 2   k count=100000
i = 2   Bindings 100000
>>>>END i = 2

>>>> i = 3
i = 3   k count=100000
i = 3   Bindings 100000
>>>>END i = 3

>>>> i = 4
i = 4   k count=100000
i = 4   Bindings 100000
>>>>END i = 4

>>>> i = 5
i = 5   k count=100000
i = 5   Bindings 100000
>>>>END i = 5

>>>> i = 6
i = 6   k count=100000
i = 6   Bindings 100000
>>>>END i = 6

>>>> i = 7
i = 7   k count=100000
i = 7   Bindings 100000
>>>>END i = 7

>>>> i = 8
i = 8   k count=100000
i = 8   Bindings 100000
>>>>END i = 8

>>>> i = 9
i = 9   k count=100000
i = 9   Bindings 100000
>>>>END i = 9

测试一些参数

示例:

java \
 -Xmx512m \
 -Xms256m \
 -Xss1m \
  -cp "target/libs/*:target/app.jar" \
  com.example.JsTest 6000 \
  --inputFor=20 \
  --enableForceGC
  • --inputFor=20
    ,for循环默认为10。您可以将for循环配置为1或20。
  • --enableForceGC
    ,你可以添加这个来强制GC。

您还可以调整 JVM 的内存设置:

  • -Xmx512m
  • -Xms256m
  • -Xss1m

您可以不断调整内存设置,看看是否有影响。

© www.soinside.com 2019 - 2024. All rights reserved.