在Java中推断方法的堆栈内存使用 [英] Inferring a method's stack memory use in Java

查看:167
本文介绍了在Java中推断方法的堆栈内存使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试确定每个方法在运行时消耗的堆栈内存量。为了完成这项任务,我设计了这个简单的程序,只强制 StackOverflowError

I'm trying to determine how much stack memory each method consumes when running. To do the task, I've devised this simple program that will just force a StackOverflowError,

public class Main {
    private static int i = 0;

    public static void main(String[] args) {
        try {
            m();
        } catch (StackOverflowError e) {
            System.err.println(i);
        }
    }

    private static void m() {
        ++i;
        m();
    }
}

打印一个整数告诉我多少次 m()被调用。我手动将JVM的堆栈大小( -Xss VM参数)设置为不同的值(128k,256k,384k),获得以下值:

printing an integer telling me how many times m() was called. I've manually set the JVM's stack size(-Xss VM parameter) to varying values (128k, 256k, 384k), obtaining the following values:

   stack    i       delta
    128     1102
    256     2723    1621
    384     4367    1644

delta由我计算,它是最后一行i和当前行之间的值。正如所料,它是固定的。这就是问题所在。据我所知,堆栈大小内存增量为128k,这样每次调用就会产生80byte的内存使用量(这似乎有些夸张)。

delta was calculated by me, and it's the value between the last line's i and the current one's. As expected it is fixed. And there lies the problem. As I know the stack size memory increment was by 128k, that yields something like a 80byte memory use per call (which seems exaggerated).

查找 m()在BytecodeViewer中,我们得到堆栈的最大深度为2.我们知道这是一个静态方法,并且没有这个参数传递, m()没有参数。我们还必须考虑返回地址指针。因此每个方法调用应该使用3 * 8 = 24个字节(我假设每个变量有8个字节,当然可能完全关闭。是吗?)。即使它比这更多,让我们说48字节,我们仍然远离80字节的值。

Looking up m() in BytecodeViewer's, we get a stack's max depth of 2. We know this is a static method and that there's no this parameter passing, and that m() has no arguments. We must also take into consideration the return address pointer. So there should be something like 3 * 8 = 24 bytes used per method call (I'm assuming 8 bytes per variable, which of course may be totally off. Is it?). Even if it's a bit more than that, let's say 48bytes, we're still far away from the 80bytes value.

我认为它可能与内存对齐有关但事实是,在这种情况下,我们会有大约64或128字节的值,我会说。

I thought it could have something to do with memory alignment, but truth is that in that case we'd have a value of roughly 64 or 128 bytes, I'd say.

我正在运行64位JVM 64位Windows7操作系统。

I'm running a 64bit JVM under a 64bit Windows7 OS.

我做了几个假设,其中一些可能完全没有。就是这样,我全都听见了。

I've made several assumptions, some of which may be totally off. Being that the case, I'm all ears.

在有人开始问我为什么要这样做之前我必须坦白......

Before anyone starts asking why I'm doing this I must be frank..

推荐答案

这个问题可能在我的脑海中浮现,也许你在更深层次上讨论这个问题,但无论如何我都会把答案抛在那里。

This question may be way over my head, perhaps you are talking about this on a deeper level, but I'll throw my answer out there anyway.

首先,你通过返回地址指针来引用什么?方法完成后,将从堆栈框架中弹出返回方法。因此,执行方法Frame中不存储返回地址。

Firstly, what do you refer to by return address pointer? When a method is finished the return method is popped from the stack frame. So no return address is stored within the executing method Frame.

方法Frame存储局部变量。因为它是静态的,无参数的,所以这些应该是空的,并且op堆栈和locals的大小在编译时是固定的,每个单元的宽度为32位。但是,除此之外,该方法还必须引用它所属的类的常量池。

The method Frame stores local variables. Since it's static, and parameterless, these should be empty as you say, and the sizes of the op stack and locals are fixed at compile time, with each unit in each being 32bits wide. But as well as this the method also must have a reference to the constant pool of the class to which it belongs.

另外,JVM规范指定方法框架可以使用其他特定于实现的信息进行扩展,例如调试信息。这可以解释剩余的字节,具体取决于编译器。

In additional the JVM spec specifies that method frames may be extended with additional implementation-specific information, such as debugging information. Which could explain the remaining bytes, depending on the compiler.

全部源自框架上的JVM规范。

更新

搜索OpenJDK来源揭示了这一点,它似乎是在方法调用时传递给Frames的结构。提供了很好的洞察力:

Scouring the OpenJDK source reveals this, which appears to be the struct that is passed to Frames on method invocation. Gives a pretty good insight on what to expect within:

/* Invoke types */

#define INVOKE_CONSTRUCTOR 1
#define INVOKE_STATIC      2
#define INVOKE_INSTANCE    3

typedef struct InvokeRequest {
    jboolean pending;      /* Is an invoke requested? */
    jboolean started;      /* Is an invoke happening? */
    jboolean available;    /* Is the thread in an invokable state? */
    jboolean detached;     /* Has the requesting debugger detached? */
    jint id;
    /* Input */
    jbyte invokeType;
    jbyte options;
    jclass clazz;
    jmethodID method;
    jobject instance;    /* for INVOKE_INSTANCE only */
    jvalue *arguments;
    jint argumentCount;
    char *methodSignature;
    /* Output */
    jvalue returnValue;  /* if no exception, for all but INVOKE_CONSTRUCTOR */
    jobject exception;   /* NULL if no exception was thrown */
} InvokeRequest;

Source

这篇关于在Java中推断方法的堆栈内存使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆