PSI性能

走着路睡觉大约 3 分钟

PSI性能

提示

IDE Perfopen in new window 插件提供了实时性能诊断工具,可以查看 缓存 视图

避免使用耗时长的PsiElement方法

尽量避免使用PsiElement中某些耗时长的方法

PsiElement.getText() 方法会遍历PSI元素的子元素树并拼接字符串,耗时较长,可以考虑使用 PsiElement.textMatches()

如果PSI元素嵌套的比较深,getTextRange(), getContainingFile(), getProject() 方法会遍历所有树直到所在文件,耗时会很长。如果你只需要PsiElement的长度,可以使用 getTextLength()

getContainingFile() 和 getProject() 通常只执行一次,然后存储在成员变量中,或者通过参数进行传递。

此外,getText()、getNode() 或 getTextRange() 等方法需要使用 AST(abstract syntax tree 抽象语法结构树),耗时可能会很长,详情见下一节。

避免同时使用多个PSI树或Document

应该避免同时在内存中加载多个PSI树或Document。理想情况下,应该只把当前打开的文件生成的 AST(abstract syntax tree 抽象语法结构树) 加载进内存中。其他所有东西,即使是为了解析或高亮显示,也可以通过使用了stubs 的 PSI 接口访问,可以减少 CPU 和内存的开销。

如果 stubs 不能解决你的问题(例如:你需要的内容占用空间很大,或很少需要它,或你正在为无法控制的PSI元素开发插件),可以创建自定义索引或gist

在生产中使用 AstLoadingFilteropen in new window 可以避免意外加载 AST ,在测试中可以使用 PsiManagerEx.setAssertOnFileLoadingFilter() 来避免。

Document 来说一样,应该只在内存中加载当前打开的文件。通常,你不需要Document 的内容,可以使用 PSI来检索你需要的元素。如果实在需要Document 内容,可以考虑把内容缓存存进自定义索引或gist 中,方便下次直接获取。如果你仍然需要 Document,至少你应该一个一个的加载并且不使用强引用(strong references),使GC能尽可能快速回收它。

缓存

执行 PsiElement.getReference(s), PsiReference.resolve(),PsiReference.multiResolve() 或computation of expression types, type inference results, control flow graphs 等耗时长的操作时,需要避免多次调用,可以缓存它们的计算结果。通常,可以使用 CachedValueopen in new window

如果缓存的信息仅取决于当前 PSI 元素的子树(没有计算或其它文件),你可以把结果缓存到PsiElement实现类的成员变量里,并在 ASTDelegatePsiElement.subtreeChanged() 的重写方法里删除缓存,部分代码如下:

//成员变量缓存name
  private volatile String myCachedName;
  @Override
  public String getName() {
    String name = myCachedName;
    // 如果有缓存,直接返回
    if (name != null) return name;
    final PsiClassStub stub = getGreenStub();
    if (stub == null) {
      PsiIdentifier identifier = getNameIdentifier();
      name = identifier == null ? null : identifier.getText();
    }
    else {
      name = stub.getName();
    }
    //没有缓存,计算后缓存 name
    myCachedName = name;
    return name;
  }


 @Override
  public void subtreeChanged() {
    dropCaches();
    super.subtreeChanged();
  }
  //清除缓存
  private void dropCaches() {
    myCachedName = null;
  }
上次编辑于:
贡献者: zhaojingbo
Loading...