缓存 java 个实例以提高性能

Caching java instance for peformance

请协助,在 java 中缓存此 line/new 实例是什么意思,例如:

XPath xpath = XPathFactory.newInstance().newXPath();

我知道我必须存储在某种记忆中...有人可以给我举个例子吗。

谢谢。

缓存意味着不要让垃圾收集器在你使用变量后将其丢弃,如果你已经知道稍后需要使用相同的变量(但 GC 不理解)。

这实际上取决于 Xpath 状态持续多长时间(可能是 function-scope、instance-scope 或 class-scope - 或者甚至更小的范围,如 for循环或 if 块,但只有你自己知道)。

下面应该有助于理解:

案例 1 - 函数内部

如果你这样做:

public Object doSomething() {
    //code...
    XPath xpath = XPathFactory.newInstance().newXPath();
    //code...
}

..然后垃圾收集器会认为一旦你离开了这个功能,你就不再需要它了,所以它很快就会把它扔掉。下次再次调用该函数时,您将不得不从头开始重建它。

案例 2 - 作为 class 字段

如果您改为这样做:

public class YourClass {
    
    private final XPath xpath = XPathFactory.newInstance().newXPath();

    public Object doSomething() {
        //code...
        this.xpath.use(...);
        //code...
    }

.. 那么每个创建的实例你只做一次工作。如果您创建 class 的 10 个实例,您将执行 10 次。如果你只创建一个,你只会做一次。 只要 该实例存在 .

,垃圾收集器就会保留每个实例的值

案例 3 - 静态字段

但如果这真的从不依赖于任何东西,那么它应该是静态的:

public class YourClass {
    private static final XPath XPATH = XPathFactory.newInstance().newXPath();
    
    public Object doSomething() {
        //code...
        XPATH.use(...);
        //code...
    }        
}

...在最后一种情况下,无论您构建了多少个 class 实例,您将始终只有一个 Xpath 实例,垃圾收集器将使该变量存活在和平 只要你的 class 被使用/位于一个 class 加载器中,其中包含使用过的 classes

(注意:一旦 ClassClassLoader 加载,静态字段就会被初始化,ClassLoader 加载 class 和许多其他的。唯一的情况是当 class 和那个 class 加载程序的所有其他 class 都变得不可访问时,class 变得有资格进行 GC。这是一个非常 hard-to-reach 的状态,这意味着通常,一旦静态字段被初始化,您就可以非常安全地在关闭应用程序之前不会收集它)。

假设上面一行代码是从一个循环中调用的:

void bar() {
 for (int i = 0; i < 10; i++) {
   XPath xpath = XPathFactory.newInstance().newXPath();
   // use xpath variable
  }
}

此处创建了 XPath 的 10 个实例。或者,您可以将 xpath 变量声明提升到循环之外,这样只会创建 1 个实例:

void bar() {
 XPath xpath = XPathFactory.newInstance().newXPath();
 for (int i = 0; i < 10; i++) {
   // use xpath variable
  }
}

这是缓存的最简单情况,即重用某些资源而不是重新创建它。

要缓存三个重要对象:

(a) 源文件。如果您对同一文档进行 运行 多次查询,您不希望为每个查询重复解析 XML 文件。解析一次,并保存生成的树。大多数人似乎都使用默认的 DOM 树模型,但也有 JDOM2 和 XOM 等替代方案 user-friendly.

(b) XPath 引擎。初始化 XPath 引擎通常很昂贵。如果您要对许多 XPath 表达式求值,则只希望执行一次。

(c) 单个 XPath 表达式。如果您需要重复执行相同的 XPath 表达式,请记住,表达式的初始编译时间可能是每次表达式求值时间的 100 倍。

所以您想将这些对象保留在内存中并尽可能重用它们。

缓存是实现此目的的一种方法。术语“缓存”通常意味着你的应用程序总是在它想要创建一个对象时发出新的请求,但是一些中间层认识到它是对已经在内存中的对象的请求,因此不需要再次创建它从零开始。

所以你可能有一个源文档缓存,这样当你的应用程序调用 Document doc = fetchDocument(filename) 时,fetchDocument 的实现会保留一个 in-memory 散列 table,并且如果文档已经存在,它会从散列 table 中获取它,否则它会从文件存储中读取并解析文件。更复杂的缓存会丢弃最近最少使用的文档,以避免不断增长的内存需求。

对于 XPath 引擎,它将是一个微不足道的 single-entry 缓存:可能类似于

XPath getXPathEngine() {
  if (xpath == null) xpath = XPathFactory.newInstance().newXPath();
  return xpath;
}

用于缓存已编译的 XPath 表达式。如果您使用 Saxon API,则不必实现自己的缓存,它会在幕后自动完成。例如,如果您这样做:

Processor proc = new Processor();
XPathCompiler xpc = proc.newXPathCompiler();
xpc.setCaching(true);
XPathExpression exp = xpc.compile("//x");

然后您可以通过直接引用它来重用已编译的表达式 exp,或者通过再次调用来编译相同的表达式,在这种情况下将从缓存中检索它。

如果您重复使用相同的 XPath 表达式,则使用变量来参数化它们:使用表达式 //x[name=$param] 并使用 $param 的不同值重复执行它,而不是构建表达式,例如"//x[name=' + param + "']" 其中每个表达式都必须重新编译。 (像这样构建表达式也会使您面临注入攻击。)