通过工具和字节码带你深入理解运行时数据区

news2025/8/12 5:50:39

上篇文章介绍了JVM运行时数据区的一些信息,这篇文章将通过工具和字节码加深对常用的堆和虚拟机栈部分的理解。

虚拟机栈再理解

下面通过3个简单的例子再深入了解一下虚拟机栈区域。

1. 虚拟机栈的出入栈过程

public class JVMStack {

    public static void main(String[] args) {
        methodA();
    }

    public static void methodA(){
        methodB();
    }

    public static void methodB(){
        methodC();
    }

    public static void methodC(){
        System.out.println("methodC()");
    }
}

这段代码很简单,在main方法中调用methodA方法、methodA调用methodB、methodB调用methodC,因为每个方法在运行期间在内存中都是以栈帧的形式表示,所以启动的时候虚拟机栈入栈过程如下:
main方法是线程中运行的,运行时先把main方法栈帧压入栈底,接着再陆续把methodA方法、methodB方法、methodC方法的栈帧压入虚拟机栈。

入栈过程

因为虚拟机栈后进先出,所以出栈顺序是相反的,methodC运行完出栈,接着就是methodB、methodA,直至main方法运行结束。

出栈过程

2. 栈帧执行流程
先看一段简单的代码:

public class FrameStacks {

    public static void main(String[] args) {
        FrameStacks frameStacks = new FrameStacks();
        frameStacks.add();
    }

    public int add(){
        int a = 2;
        int b = 3;

        int c = (a + b)*4;
        return c;
    }
}

这段代码很简单,就是a和b相加的结果乘以2,然后返回。那这段代码在JVM是怎么运行的呢。先看下栈帧的结构图

栈帧内存结构

因为add是个实例方法,所以局部变量表中第0位索引的变量槽默认是用于传递方法所属对象实例的引用,在方法中可以通过关键字“this”来访问到这个隐含的参数。也可以通过javap -v FrameStacks.class 来看下局部变量表结构,可以看到第0位索引存放的是this。

局部变量表

再通过javap -c FrameStacks.class命令看下add方法的字节码:

public int add();
    Code:
       0: iconst_2
       1: istore_1
       2: iconst_3
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: iconst_4
       8: imul
       9: istore_3
      10: iload_3
      11: ireturn

先简单了解下这几个字节码的意思:

  1. iconst_<n> :将一个int类型常量加载到操作数栈,n为将要操作的数值或者常量池行号
  2. istore_<n> :将一个int类型数值从操作数栈存储到局部变量表,n为局部变量的位置序号
  3. iload_<n> :将一个局部变量加载到操作栈,n为局部变量的位置序号
  4. iadd :int类型加法指令,运算后的结果自动入操作数栈
  5. imul : int类型乘法指令,运算后的结果自动入操作数栈
  6. ireturn :返回

再解释一下这段字节码的执行步骤:

0: iconst_2 -> 将a=2加载到操作数栈
1: istore_1 -> 把a从操作数栈出栈并储存到局部变量表下标为1的位置
2: iconst_3 -> 将b=3加载到操作数栈
3: istore_2 -> 把b从操作数栈出栈并储存到局部变量表下标为2的位置 
4: iload_1  -> 把a从局部变量表加载到操作数栈
5: iload_2  -> 把b从局部变量表加载到操作数栈
6: iadd     -> 把操作数栈栈顶的两个值a和b相加,相加的动作是在执行引擎做的,加完之后自动入操作数栈
7: iconst_4 -> 把常量4加载到操作数栈栈顶
8: imul     -> 把a和b的和乘以4,同样在执行引擎计算,计算之后自动入栈顶
9: istore_3 -> 把imul得到的结果c从操作数栈出栈并存储到局部变量表下标为3的位置
10: iload_3 -> 把c从局部变量表加载到操作数栈
11: ireturn -> 把操作数栈中的结果c返回

通过这些执行步骤可以发现,变量会频繁的出入操作数栈,一些运算操作也是在执行引擎进行的,操作数栈只是暂存变量。其实操作数栈就类似于我们说的缓存,出入栈就是删除和添加缓存,操作数栈是线程级别的缓存,随着线程的结束操作数栈也就over了。

3. 栈帧优化

先介绍一个工具JHSDB,JHSDB是一款基于服务性代理(Serviceability Agent,SA)实现的进程外调试工具。服务性代理是 HotSpot虚拟机中一组用于映射Java虚拟机运行信息的、主要基于Java语言(含少量JNI代码)实现的 API集合。通过JHSDB可以更好的理解栈帧优化。

3.1 JHSDB的启动
要使用必须要把sawindbg.dll复制一份到jre的bin目录下,我的jdk安装目录如下图,你的可能不一样:

jdk文件夹

jdk1.8.0_152\jre\bin目录下找到sawindbg.dll文件,复制一份到jre\bin目录下。
进入jdk1.8.0_152\lib目录,通过命令行执行java -cp .\sa-jdi.jar sun.jvm.hotspot.HSDB ,执行完时候会出现如下窗口

HSDB

3.2 修改上面例子的代码

public class FrameStacks {

    public static void main(String[] args) throws InterruptedException {
        FrameStacks frameStacks = new FrameStacks();
        frameStacks.add(4);
    }

    public int add(int c) throws InterruptedException {
        int a = 2;
        int b = 3;

        int d = (a + b)*c;
        Thread.sleep(Integer.MAX_VALUE);
        return d;
    }
}

修改后启动main方法

3.3 JHSDB的使用
在cmd命令行窗口输入jps 命令,找到进程ID

jps

打开JHSDB窗口
file

HotSpot process

打开之后可以看到有好几个线程启动,我们只要选择main线程就行,然后选择左上角图片是服务器按钮查看栈内存

Stack Memory

一个栈帧的开始是从Interpreted frame部分开始的。
第一个栈帧是当前正在执行的栈帧,在这里是Thread.sleep方法的栈帧,sleep方法是native方法,因此当前是本地方法栈,也从侧面证明了Hotspot虚拟机的本地方法栈和虚拟机栈是合二为一的。
第二个是方法add方法的栈帧、第三个是main方法的栈帧,可以看到add方法栈帧的局部变量表(locals area)部分和main方法栈帧的操作数栈(expression stack)有重合,也就是蓝色方框部分,这段区域就是共享部分,也是Hotspot虚拟机对栈帧的优化。

栈帧优化

堆区再理解

下面通过JHSDB工具来再理解一下堆区的内存布局。
新建一个类HeapObject

package jvm;

public class HeapObject
 {

    public static void main(String[] args) throws InterruptedException {

        Student studentSun = new Student();
        studentSun.setUsername("sun");
        studentSun.setAge(18);

        for (int i = 0; i < 15; i++) {
            System.gc();
        }

        Student studentArron = new Student();
        studentArron.setUsername("Arron");
        studentArron.setAge(17);

        Thread.sleep(Integer.MAX_VALUE);
    }
}

class Student{

    private String username;

    private Integer age;

    // 省略get/set方法
}

在idea中Edit Configurations中添加虚拟机启动参数-XX:+UseConcMarkSweepGC -XX:-UseCompressedOops -Xmx10m,如图:

VM option

-XX:+UseConcMarkSweepGC的作用是使用CMS垃圾收集器。这样能更好的查看堆的分代情况,关于CMS垃圾收集器可自行了解,这里不做过多解释。
-XX:-UseCompressedOops 禁止指针压缩,JHSDB对指针压缩存在缺陷,建议关闭指针压缩
-Xmx10m是设置堆的最大内存为10M,在这里是为了JHSDB加快在内存中搜索对象的速度

然后在通过jps命令查看HeapObject进程ID

Img

进程id获取到之后通过JHSDB查看具体信息,在Tools -> Object Histogram中查看类的描述信息,通过类的全限定名搜索Student类。

Object Histogram

找到之后双击查看类的描述,这里new 了两个Student对象,会看到两个对象信息。

Object of Type

然后通过下方的Inspect 按钮分别查看两个对象地址对应的哪个对象

Inspector

从Inspector中我们可以看到
studentSun对象的内存地址是0x0000000013832558
studentArron对象的内存地址是0x0000000013400000

再在Tools -> Heap Parameters中查看堆内存分代情况

Heap Parameters

eden区的起始地址:[0x0000000013400000 ~ 0x00000000136b0000)

from 起始地址:[0x00000000136b0000 ~ 0x0000000013700000)

to 起始地址:   [0x0000000013700000 ~ 0x0000000013750000)

Tenured 起始地址: [ 0x0000000013750000 ~ 0x0000000013e00000)

对比一下studentArron对象的内存地址是在Eden区的范围内的,所以studentArron对象在Eden区,studentSun对象内存地址在Tenured区老年代的范围内,所以studentSun在Tenured区。

为什么这两个对象不在一个区呢?
这是因为在代码中显示调用了System.gc(),studentSun对象的分代年龄变大了又因为studentSun对象的引用还在被使用,所以就把它放到了Tenured区。

从这个例子中我们可以看到Hotspot堆内存结构目前使用的是分代划分,内存空间也是连续的,并且虽然Student类对象虽然是局部变量,但是实例还是在堆区分配的。

总结

本文通过JHSDB工具和字节码层面来更深入的了解JVM运行时数据区,对于JHSDB工具和字节码也只是一个简单的使用和说明,感兴趣的可以再深入了解一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/15207.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【JVM学习】Jconsole 配置jmx 监控JVM

1. 配置jvm启动参数 在使用jconsole监控jvm之前&#xff0c;首先需要先打开jmx。启动jvm时&#xff0c;添加以下参数。jmx通过58088端口对外开放。 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port58088 -Dcom.sun.management.jmxremote.sslfalse -Dcom…

低代码能为企业带来什么好处

低代码自问世以来&#xff0c;逐步进入到公众视野&#xff0c;由于其可视化的操作界面、可以快速开发应用等优势&#xff0c;让企业在数字化转型过程中得到技术和速度的支撑。在低代码平台&#xff0c;技术人员和业务专业人员可通过图形界面&#xff08;而不是传统的计算机编程…

m基于matlab的OQPSK载波同步通信系统仿真,载波同步采用costas环

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 OQPSK调制技术是一种恒包络调制技术&#xff0c;受系统非线性影响小&#xff0c;具有较高的带宽利用率和功率利用率&#xff0c;在卫星环境、无线环境下得到广泛应用。因此&#xff0c;在…

案例分析 丨湖仓一体助力保险企业数据战略转型升级

当下&#xff0c;海量数据结合前沿技术架构正在为保险业带来根本性的变革。本文以某知名保险机构为例&#xff0c;结合偶数行业实践经验&#xff0c;介绍保险企业如何利用湖仓一体技术推动数据战略转型升级。 背景介绍 在对该客户需求进行深度挖掘并横向比较行业现状后&#xf…

基于PHP+MySQL的图书分享平台

读书是提高一个人思维,最直接,最简单和最方便的方式。同时读书可以让自己的思想得到提升,保证自己一直都在时代的检前沿。图书是知识的载体,但是它也有它的特殊性。那就是他可以进行分享。很多时候,人们在购买图书并进行阅读之后,很可能就不再需要了,这个时候就可以和其他人进行…

SpringBoot完成查询和增加功能(简易版)

目录 一、环境准备 1、db 2、html 打开前端开发工具HBuilder X&#xff0c;把准备好的前端spboot移进去&#xff1a; 接着查看电脑的mvn版本&#xff0c;选中项目右键&#xff1a; npm -version npm i​ 3、java 在弹出的窗口中选择你需要导入的项目&#xff0c;点击ok…

python的网络请求库urllib、urllib2、urllib3、request的联系

文章目录1. 简介2. urllib3. urllib24. urllib35. requests6. 相关文章1. 简介 urllib、urllib2、urllib3、request均能通过网络访问互联网上的资源文件&#xff0c;它们通过使用统一资源定位符&#xff08;URL&#xff09;并结合re模块完成很多意想不到的操作。 urllib&…

数据库与缓存的一致性

数据库与缓存的一致性 一致性就是数据保持一致&#xff0c;在分布式系统中&#xff0c;可以理解为多个节点中数据的值是一致的。 强一致性&#xff1a;这种一致性级别是最符合用户直觉的&#xff0c;它要求系统写入什么&#xff0c;读出来的也会是什么&#xff0c;用户体验好…

Windows系统怎么加密文件夹?

Windows系统是目前使用人数最多的电脑系统&#xff0c;那么你知道在Windows系统上怎么加密文件夹吗&#xff1f;下面这篇文章&#xff0c;将向大家介绍两种Windows系统文件夹加密的方法&#xff0c;希望对你有所帮助。 方法一&#xff1a;使用超级加密3000进行加密 1、打开超级…

C和C++导出DLL后在Csharp中调用函数名的差异

参考链接&#xff1a;cc​​​​​​​在Unity中调用C代码&#xff1a;出现EntryPointNotFoundException的解决办法&#xff1a; - 知乎 (zhihu.com) __declspec(dllexport)的位置问题 - 岚之山 - 博客园 (cnblogs.com) 如果是报如下异常 System.EntryPointNotFoundExceptio…

【GlobalMapper精品教程】017:KML generator快速将坐标转为KML文件

本文介绍KML generator软件,并快速将坐标转为KML文件的使用方法,并用globalmapper中打开kml文件加以验证。本专栏配套完整的案例数据包,请打开data017.rar获取软件及数据。 文章目录 1. KML文件介绍2. kml generator软件介绍2.1 单点KML制作2.2 Excel数据KML制作2.3 文本文件…

【模电实验】【超值1 + 1】【验证性实验——分立元件“OTL“功率放大器实验】【验证性实验——分立元件稳压电源实验】

实验9-1 验证性实验——分立元件"OTL"功率放大器实验 Multisim仿真 编辑分立元件OTL功率放大器的仿真电路如下&#xff1a; 静态工作点的调试与仿真如下&#xff1a; 发射极基极集电极T1277mV998mV5.66VT26.38V6.93V12VT36.20V5.66V0V 交越失真分析如下&#xff1a…

【Linux】OS和进程概念

文章目录1.冯诺依曼体系结构2. 操作系统&#xff08;Operator System &#xff09;概念总结3. 进程描述进程-PCBtask_struct-PCB的一种task_ struct内容分类见见进程1.冯诺依曼体系结构 冯诺依曼结构也称普林斯顿结构&#xff0c;是一种将程序指令存储器和数据存储器合并在一起…

微机原理实验:字符转换为ASCII码

随记&#xff01; 要求&#xff1a; 1. 将指定数据区的字符串数据以ASCII码形式显示在屏幕上&#xff0c;并通过DOS功能调用完成必要提示信息的显示。 2. 在屏幕上显示自己的学号姓名信息。 3. 循环从键盘读入字符并回显在屏幕上&#xff0c;然后显示出对应字符的AS…

JS高级(四):正则表达式、常见的特殊字符、案例、预定义类、正则替换

JavaScript高级&#xff08;四&#xff09;一、正则表达式1.它有什么作用&#xff1f;2.创建正则表达式&#xff08;1&#xff09;利用RegExp对象来创建&#xff08;2&#xff09;利用字面量创建3.测试正则表达式二、特殊字符1.^和$匹配开头和结尾2.[ ]字符类3.量词符三、用户名…

有线热电偶温度验证系统

在本文中&#xff0c;我们将介绍使用有线热电偶温度验证系统的一些优势&#xff0c;并将虹科Ellab的E-Val™ Pro与市场上可用的传统有线系统&#xff08;验证仪&#xff09;进行比较分析。 虹科E-Val Pro是一个高度直观的基于电缆的系统。设计用于各种应用&#xff0c;如冻干、…

数字化时代,企业为什么需要商业智能BI?

数字化时代的到来&#xff0c;数据资产已经成为第五大生产要素&#xff0c;数据是企业生产、经营和决策的重要因素。为提升企业经营状况的感知能力&#xff0c;数据分析应运而生&#xff0c;数据获取的及时性、准确性也需要更加系统些&#xff0c;此时&#xff0c;成熟的商业智…

EasyExcel代码层面设置写出的Excel样式、以及拦截器策略的使用、自动列宽设置、EasyExcel默认设置详解

一、概述 虽然EasyExcel已经提供了一系列注解方式去设置样式。 但是如果没有实体类&#xff0c;或者想要更精确的去设置导出文件的Excel样式的时候就需要在代码层面去控制样式了。 二、使用已有拦截器自定义样式 主要步骤&#xff1a; 创建Excel对应的实体对象创建一个sty…

注意 ! !|95% 的应用程序中发现错误配置和漏洞

业内权威机构 Synopsys 最近发布了一项研究报告&#xff0c;结果表明在进行4300次测试后&#xff0c;发现95%的应用程序中都至少都有一个影响安全的漏洞或配置错误&#xff0c;其中高危漏洞占20%&#xff0c;严重漏洞则占4.5%。在此次研究中&#xff0c;82% 的测试目标是 Web 应…

调用链路上千条,如何观测 Nacos 的运行状态

作者&#xff1a;涌月 背景 随着近年来微服务体系发展&#xff0c;微服务上下游链路的越来越复杂&#xff0c;在阿里云的线上实践场景中&#xff0c;我们发现使用微服务架构的公司的业务动辄会出现上千条调用链路&#xff0c;排查问题代价巨大。 在这种背景下&#xff0c;阿…