JVM线程栈

JVM栈中以LIFO方式存储栈帧,每个方法调用对应一个栈帧,包含局部变量数组、操作数堆栈和动态链接。栈帧在方法调用和结束时动态生成及销毁,用于保存和恢复执行状态。局部变量数组存储原始类型、对象引用和返回地址,操作数堆栈用于数据交换,动态链接则指向运行时常量池,实现符号到实际内容的转换。

JVM栈中存放了一组frame(栈帧),以LIFO的方式存储,方法调用时生成一个frame,方法执行结束时删除一个frame。frame中存放方法调用时调用者的执行状态快照,在被调方法执行结束后,用于恢复方法调用前的程序执行状态。栈帧的概念在c或c++语言编译出来的汇编语言中也存在,用ESP和EBP定义栈顶和栈底。汇编和JVM的栈帧功能都一样,只是汇编的frame直接给cpu看,而JVM里的frame是给JVM看,JVM会在不同硬件设备上实现不同的栈帧逻辑。

c语言在做函数调用时,栈帧的实现会更精细:

1. 在函数调用时,调用者先将入参按照从右到左的顺序,依次推入堆栈。

2. 将函数退出时的指令返回地址推入堆栈(调用者函数调用的下一行命令)。

3. 将当前ebp推入堆栈。

4. 将esp赋值给ebp,这是新栈帧的起点。

5. call命令修改eip,使得程序执行跳转到被调函数,之后在ebp之下存放被调用函数内的局部变量。

6. 函数退出时,使用leave命令,将ebp赋值给esp,被调函数的栈帧失效。

7. ret命令将当前esp的前一个四字节数据推给eip,这是之前存储的返回地址。

8. 函数调用结束,程序继续执行。

9. 函数的返回值通常放在寄存器中传递。

函数调用时的栈使用情况如下图:

JVM中的栈帧是抽象的概念,与具体的汇编行为无关。frame由局部变量数组、操作数堆栈、动态链接组成,其中局部变量数据和操作数堆栈的最大深度在编译阶段就要确定,并存储在.class文件里,分别由max_locals和max_stack表示(CodeAttribute),这样frame里的局部变量数组大小才能确定 。对一个线程而言,同一时间只有最新的一帧处于活动状态,新的方法调用会创建新帧,方法正常或异常退出会回收当前帧。

局部变量数组中存放着原始数据类型、对象引用和返回地址(jsr, ret, jsr_w时使用),其中long和double用两个局部变量存储,其它类型用一个。本地变量通过index寻址,index从0开始。方法的入参存放在本地变量数据的开头,其中类方法调用时,index=0处存放着第一个入参。对象方法调用时,index=0处存放this指针,指向调用该方法的对象,index=1处存放第一个入参。

操作数堆栈为线程提供堆栈服务,LIFO,也是数据交换的区域。Java字节码支持从局部变量数组和常量值中加载数据到操作数堆栈,也可以加载对象的成员变量,另外还提供指令处理堆栈里的数据。操作数堆栈就是一张草稿纸。

动态链接是frame的重要组成部分,指向运行时常量池。常量池中存放的符号是字节码运行的必要条件。一些操作数的字节码,其操作数就是常量池中一个编号,动态链接使得这些编号能被转化成实际内容,从而指导字节码调用所需方法,获取实际数值,加载未被加载的类。

 

在 Java 虚拟机 (JVM) 中,线程大小的配置可以通过 `-Xss` 参数实现。此参数用于指定每个线程所拥有的空间大小。合理的配置能够帮助开发者避免因溢出 (`StackOverflowError`) 或资源耗尽而导致的问题。 ### 1. 配置线程大小的方法 通过命令行启动 JVM 时可以直接传入 `-Xss` 参数来设定线程大小。例如: ```bash java -Xss512k MyApplication ``` 上述命令将每个线程大小设置为 512 KB。同样地,也可以使用其他单位(如 MB)进行配置,比如 `-Xss1m` 表示每个线程大小为 1 MB[^1]。 除了 `-Xss` 外,还可以使用 `-XX:ThreadStackSize` 来完成同样的目的。这两个选项功能基本一致,但在不同场景下可能有不同的习惯用法。例如: ```bash java -XX:ThreadStackSize=512 MyApplication ``` 这里设置了每个线程大小为 512 KB[^3]。 需要注意的是,默认情况下 JVM 会根据目标平台自动选择一个适合的线程大小。如果未显式定义,则会采用默认值。例如,在某些 64 位 Linux 平台上,默认值可能是 1024 KB。 ### 2. 查看当前 JVM线程大小 要确认正在运行的 JVM 实例中的线程大小,可以借助以下几种工具和技术: #### 方法一:使用 `jinfo` 工具 `jinfo` 是 JDK 自带的一个实用程序,可用于动态查询和修改 JVM 运行期参数。假设我们知道目标进程 ID (PID),那么就可以这样操作: ```bash jinfo -flag Xss <pid> ``` 这将返回类似于下面的结果: ``` -Xss512k ``` #### 方法二:读取 `/proc/<pid>/maps` 文件(仅限 Linux) 对于基于 Unix/Linux 的系统而言,我们还能直接查阅底层内存映射文件获取相关信息。不过这种方法相对复杂一些,并且需要一定的权限才能成功执行。 #### 方法三:打印日志或调试信息 另一种简单粗暴的办法是在代码内部加入如下片段以输出当前 JVM 设置情况: ```java public class PrintJvmSettings { public static void main(String[] args) { System.out.println("Thread Stack Size: " + getThreadStackSize()); } private static String getThreadStackSize() { try { RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean(); List<String> arguments = runtimeMxBean.getInputArguments(); for (String arg : arguments) { if (arg.contains("-Xss")) { return arg; } } } catch (Exception e) { e.printStackTrace(); } return "Not Found"; } } ``` 这段代码遍历了输入参数列表寻找匹配项并将其显示出来[^2]。 ### 总结 无论是通过外部工具还是内置 API,都可以方便快捷地了解 JVM 当前线程大小的具体数值。合理调整这一参数有助于平衡性能与稳定性之间的关系,特别是在高并发环境下显得尤为重要。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值