如何判断一段java代码是否无堆分配?

如何判断一段java代码是否无堆分配?,java,Java,我有一个java函数,它的文档声称它没有堆内存分配 如何在运行时验证它是否(仍然)按声明的方式运行 理想情况下,我希望在正常(生产)程序运行期间执行此操作, 以一种程序可以自省并显示为程序输出的方式 我希望有一个简单可靠的运行时API,如: 设置检查点A,调用函数,设置检查点B,询问在A和B之间的这个线程中是否有堆内存分配 或者,临时启用“如果在此线程中发生堆内存分配,则引发异常”模式 如果这是不可能的,我希望至少能够在某种调试模式/评测运行期间执行相同的操作 我主要感兴趣的是在运行时做这件

我有一个java函数,它的文档声称它没有堆内存分配

如何在运行时验证它是否(仍然)按声明的方式运行

理想情况下,我希望在正常(生产)程序运行期间执行此操作, 以一种程序可以自省并显示为程序输出的方式

我希望有一个简单可靠的运行时API,如:

  • 设置检查点A,调用函数,设置检查点B,询问在A和B之间的这个线程中是否有堆内存分配
  • 或者,临时启用“如果在此线程中发生堆内存分配,则引发异常”模式
如果这是不可能的,我希望至少能够在某种调试模式/评测运行期间执行相同的操作

我主要感兴趣的是在运行时做这件事, 但如果有可能使用静态分析工具来实现,我也会对此感兴趣

我在网上搜索了一下,什么也没找到。有内存分析器,例如。 可以在任何给定时间报告和分解当前堆使用情况, 但我没有看到任何证据表明他们能回答这个简单而精确的是或否
“在此间隔期间此线程中是否发生任何分配”查询。

您可以通过使用、禁用GC(
-XX:+usepsilongc
)和仅运行要测试的逻辑来分析受控环境中的内存使用情况

// Logic to test (to load classes, do one-off initializations)
Runtime runtime = Runtime.getRuntime();
long start = runtime.freeMemory();
// Logic to test
long end = runtime.freeMemory();
assert start == end;

我认为除了检查方法中的
new
实例及其调用的任何方法之外,没有其他方法可以测试这一点

内存分配测试:无法隔离该方法的影响

基准测试:您可以单独测试运行该方法,但是如果生产中的分配是基于存在的其他资源有条件地进行的,例如

if (MyOtherClass.myOtherVariable != null) {
  int[] arr = new int[100];
}

在这种情况下,内存分配取决于可能发生或可能没有发生的其他步骤。

您完全可以通过运行时检测来完成这项工作。您可能无法为程序的某个独立部分执行此操作,但您可以为程序的某个独立运行(或生产中的运行采样)设置类似的设置。例如,您可以这样使用它:

public class Test {
  public static void main(String [] args) throws Exception {
    AtomicInteger allocations = new AtomicInteger();

    AllocationRecorder.addSampler(new Sampler() {
      public void sampleAllocation(int count, String desc, Object newObj, long size) {
         allocations.getAndIncrement();
      }
    });
    codeBlock(); // do any initialization, classloading first!
    int expectedAllocations = allocations.get();
    codeBlock();
    assert allocations.get() == expectedAllocations; // no more allocations
  }
}

但这在多线程环境中不起作用,对吗?@fluffy只在基准测试(OP称之为调试模式/评测运行)中起作用。这看起来很有希望,尤其是在底层技术中。但是为什么你会说“你可能无法为你的项目的一个孤立部分做这件事”?我的意思是,这可能会影响其余代码的性能:没有一种好方法可以确保回调只在您想要的代码上运行;每次分配都会运行它。您可以将回调设置为仅在“打开”采样的情况下执行任何操作,但性能仍然会受到影响。啊,我明白了。如果我能让它工作的话,它看起来能很好地满足我的需要。你能用它吗?在Linux上,使用openjdk14,按照“入门”页面上的说明,我既不能在本地构建它(
mvn package
使用“没有看到正确数量的基本函数预期:但是是:”),也不能在本地构建预构建的jar(
java-javaagent:path/to/allocation.jar Test
使用失败)“未能在com.google.monitoring.runtime.instrumentation.asm.ClassReader中插入类:java.lang.IlligalArgumentException。”(使用堆栈跟踪)。我找到了一些巧妙的方法:(1)将预构建的jar(3.0)与java8(而不是java11或java14)一起使用——这很有效。(2)将-DskipTests添加到
mvn package
命令中;它仍然失败(在构建文档时),但至少它首先在target/dir中构建了一个可用的.jar文件,即使在当前的java版本中也能工作。在我最终获得该实用工具的工作版本后,它工作得非常出色。接受!