当前位置: 首页 > news >正文

室内设计网站导航/软文写作的十大技巧

室内设计网站导航,软文写作的十大技巧,外卖网站建设的策划方案,普宁17网站一起做淘宝文章目录1、字节码结构1.1、基本结构1.2、实际观测2、内存表示3、方法调用指令4、invokedynamicEND结语Java真的是长盛不衰,拥有顽强的生命力。其中,字节码机制功不可没。字节码,就像是 Linux 的 ELF。有了它,JVM直接摇身一变&…

文章目录

    • 1、字节码结构
      • 1.1、基本结构
      • 1.2、实际观测
    • 2、内存表示
    • 3、方法调用指令
    • 4、invokedynamic
    • END
  • 结语

Java真的是长盛不衰,拥有顽强的生命力。其中,字节码机制功不可没。字节码,就像是 Linux 的 ELF。有了它,JVM直接摇身一变,变成了类似操作系统的东西。

要学习字节码,不能仅仅靠看枯燥的文档。本文会介绍几个有用的工具,可以非常容易的上手,来实际观测class文件这个小魔兽,助你搲的更深一些。

1、字节码结构

1.1、基本结构

在开始之前,我们先简要的介绍一下class文件的内容。这个结构,可以使用jclasslib工具来查看。

图片

上图是class文件基本内容。这部分内容枯燥乏味,关于它的细节在Java的官方都能非常容易的找到。

如下图,展示了一个简单方法的字节码描述,我们可以看到真正的执行指令在整个文件结构中的具体位置。

图片

1.2、实际观测

为了让大家避免避免枯燥的二进制对比分析,直接定位到真正的数据结构,这里介绍一个小工具,使用这种方式学习字节码会节省很多时间。

https://wiki.openjdk.java.net/display/CodeTools/asmtools

这个工具就是asmtools,执行下面的命令,将看到类的 JCED 语法结果。

java -jar asmtools-7.0.jar jdec LambdaDemo.class

输出的结果类似于下面的结构,它与我们上面介绍的字节码组成是一一对应的,对照官网或者书籍,学习速度飞快。

class LambdaDemo {0xCAFEBABE;0; // minor version52; // version[] { // Constant Pool; // first element is emptyMethod #8 #25; // #1InvokeDynamic 0s #30; // #2InterfaceMethod #31 #32; // #3Field #33 #34; // #4String #35; // #5Method #36 #37; // #6class #38; // #7class #39; // #8Utf8 "<init>"; // #9Utf8 "()V"; // #10Utf8 "Code"; // #11

了解了类的文件组织方式,下面我们来看一下,类文件在加载到内存中以后,是一个什么表现形式。

2、内存表示

准备以下代码,使用javac -g InvokeDemo.java进行编译。然后使用java命令执行。程序将阻塞在sleep函数上,我们来看一下它的内存分布。

interface I {default void infMethod() { }void inf();
}abstract class Abs {abstract void abs();
}public class InvokeDemo extends Abs implements I {static void staticMethod() { }private void privateMethod() { }public void publicMethod() { }@Overridepublic void inf() { }@Overridevoid abs() { }public static void main(String[] args) throws Exception{InvokeDemo demo = new InvokeDemo();InvokeDemo.staticMethod();demo.abs();((Abs) demo).abs();demo.inf();((I) demo).inf();demo.privateMethod();demo.publicMethod();demo.infMethod();((I) demo).infMethod();Thread.sleep(Integer.MAX_VALUE);}
}

为了更加明显的看到这个过程,下面介绍一下 jhsdb 这个工具,这是在 Java9 之后 JDK 先加入的调试工具,我们可以在命令行使用 jhsdb hsdb 来启动它。注意,要加载相应的进程时,必须确保是同一个版本的应用进程,否则会产生报错。

图片

attach启动后的Java进程后,可以在 Class Browser 菜单查看加载的所有类信息。我们在搜索框输入 InvokeDemo,找到要查看的类。

图片

**@*符号后面的,就是具体的内存地址,我们可以复制一个,然后在*Inspector 视图查看具体的属性。可以大体认为这就是类在方法区的具体存储。

图片

在Inspector视图中,我们找到方法相关的属性 _methods,可惜的是它无法点开,也无法查看。

图片

接下来可以使用命令行来检查这个数组里面的值。打开菜单中Console,然后输入examine命令。可以看到这个数组里的内容,对应的地址就是Class视图中的方法地址。

examine 0x000000010e650570/10

图片

我们可以在Inspect视图看到方法所对应的内存信息,这确实是一个Method方法的表示。

图片

相比较起来,对象就简单的,它只需要保存一个到达Class对象的指针即可。我们需要先从对象视图进入,然后找到它,一步步进入Inspect视图。

图片

由以上的这些分析,我们可以得出下面这张图。执行引擎想要运行某个对象的方法,需要先在栈上找到这个对象的引用,然后再通过的对象的指针,找到相应的方法字节码。

图片

3、方法调用指令

关于方法的调用,Java一共提供了5个指令,用来调用不同类型的函数。

  1. invokestatic 用来调用静态方法的。
  2. invokevirtual 用于调用非私有实例方法,比如public和protected。大多数方法调用属于这一种。
  3. invokeinterface 和上面这条指令类似,不过是作用于接口类。
  4. invokespecial 用于调用私有实例方法、构造器,以及super关键字等。
  5. invokedynamic 用于调用动态方法。

我们依然使用上面的代码片段看一下前四个指令的使用场景。代码中包含一个接口I,一个抽象类Abs,一个实现和继承了两者的类InvokeDemo

参考Java的类加载机制,在class文件被加载到方法区以后,就完成了从符号引用到具体地址的转换过程。

我们可以看一下编译后的main方法字节码。尤其需要注意的是对于接口方法的调用。使用实例对象直接调用,和强制转化成接口调用,所调用的字节码指令分别是 invokevirtualinvokeinterface,它们是不同的。

public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=2, args_size=10: new           #2                  // class InvokeDemo3: dup4: invokespecial #3                  // Method "<init>":()V7: astore_18: invokestatic  #4                  // Method staticMethod:()V11: aload_112: invokevirtual #5                  // Method abs:()V15: aload_116: invokevirtual #6                  // Method Abs.abs:()V19: aload_120: invokevirtual #7                  // Method inf:()V23: aload_124: invokeinterface #8,  1            // InterfaceMethod I.inf:()V29: aload_130: invokespecial #9                  // Method privateMethod:()V33: aload_134: invokevirtual #10                 // Method publicMethod:()V37: aload_138: invokevirtual #11                 // Method infMethod:()V41: aload_142: invokeinterface #12,  1           // InterfaceMethod I.infMethod:()V47: return

另外还有一点,和我们想象中的不同,大多数普通方法调用,使用的是 invokevirtual 指令,它其实是和invokeinterface 一类的,都属于虚方法调用。很多时候,JVM需要根据调用者的动态类型,来确定调用的目标方法,这就是动态绑定的过程。

invokevirtual指令有多态查找的机制,该指令的运行时解析过程步骤如下:

  1. 找到操作数栈顶的第一个元素所指向的对象的实际类型,记做c。
  2. 如果在类型c中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束,不通过则返回java.lang.IllegalAccessError。
  3. 否则,按照继承关系从下往上依次对c的各个父类进行第二步的搜索和验证过程。
  4. 始终没找到合适的方法,抛出java.lang.AbstractMethodError异常。这就是java语言中方法重写的本质。

相对比,invokestatic指令,加上invokespecial指令,就属于静态绑定过程。

所以静态绑定,指的是能够直接识别目标方法的情况,而动态绑定指的是需要在运行过程中根据调用者的类型来确定目标方法的情况。

可以想象,相对于静态绑定的方法调用来说,动态绑定的调用就更加耗时一些。由于方法的调用非常的频繁,JVM对动态调用的代码进行了比较多的优化。比如使用方法表来加快对具体方法的寻址,以及使用更快的缓冲区来直接寻址( 内联缓存)。

4、invokedynamic

有时候在写一些python脚本或者js脚本的时候,会特别羡慕这些动态语言。如果把查找目标方法的决定权,从虚拟机转嫁给用户代码,我们就会有更高的自由度。

我们单独把invokedynamic抽离出来介绍,是因为它比较复杂。和反射类似,它用于一些动态的调用场景,但它和反射有着本质的不同,效率也比反射要高的多。

这个指令通常在lambda语法中出现,我们来看一下一小段代码。

public class LambdaDemo {public static void main(String[] args) {Runnable r = () -> System.out.println("Hello Lambda");r.run();}
}

使用javap -p -v 命令可以在main方法中看到invokedynamic指令。

public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=1, locals=2, args_size=10: invokedynamic #2,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;5: astore_16: aload_17: invokeinterface #3,  1            // InterfaceMethod java/lang/Runnable.run:()V12: return

另外,我们在javap的输出中找到了一些奇怪的东西。

BootstrapMethods:0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;Method arguments:#28 ()V#29 invokestatic LambdaDemo.lambda$main$0:()V#28 ()V

BootstrapMethods属性在Java1.7以后才有,位于类文件的属性列表中,这个属性用于保存 invokedynamic 指令引用的引导方法限定符。

和上面介绍的四个指令不同,invokedynamic并没有确切的接收对象,取而代之的,是一个叫做 CallSite 的对象。

static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type);

其实,invokedynamic指令的底层,是使用方法句柄(MethodHandle)来实现的。方法句柄是一个能够被执行的引用,它可以指向静态方法和实例方法,以及虚构的get和set方法,从IDE中可以看到这些函数。

图片

句柄类型(MethodType)就是我们对方法的具体描述,配合方法名称,能够定位到一类函数。访问方法句柄和调用原来的指令是基本一致的,但它的调用异常,包括一些权限检查,是在运行时才能被发现的。

lambda语言实际上是通过方法句柄来完成的,在调用链上自然也多了一些调用步骤,那么在性能上,是否就意味着lambda性能低呢?对于大部分“非捕获”的lambda表达式来说,JIT编译器的逃逸分析能够优化这部分差异,性能和传统方式无异;但对于“捕获型”的表达式来说,就需要通过方法句柄,不断的生成适配器,性能自然就低了很多(不过和便捷性相比,一丁点性能损失是可接受的)。

除了lambda表达式,我们还没有其他的方式来产生invokedynamic指令。但是我们可以使用一些外部的字节码修改工具,比如ASM,来生成一些带有这个指令的字节码,这通常能够完成一些非常酷的功能,比如完成一门弱类型检查的JVM-Base语言。

END

本文从Java字节码的顶层结构介绍开始,通过一个实际代码,了解了类加载以后,在JVM内存里的表现形式,并了解了jhsdb对Java进程的观测方式。

我们了解到Java7之后的invokedynamic指令,它实际上是通过方法句柄来实现的。和我们关系最大的就是Lambda语法,了解了这些原理,可以忽略那些对Lambda性能高低的争论,要尽量写一些“非捕获”的Lambda表达式。

什么?你问什么叫非捕获?那就需要你自己搲了。

原创:小姐姐味道, https://mp.weixin.qq.com/s/c2CAAUgjnYwI0_1w58Q-ww

结语

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、评论、收藏➕关注,您的支持是我坚持写作最大的动力。

相关文章:

Java字节码深度知多少?

文章目录1、字节码结构1.1、基本结构1.2、实际观测2、内存表示3、方法调用指令4、invokedynamicEND结语Java真的是长盛不衰&#xff0c;拥有顽强的生命力。其中&#xff0c;字节码机制功不可没。字节码&#xff0c;就像是 Linux 的 ELF。有了它&#xff0c;JVM直接摇身一变&…...

【C++】智能指针(万字详解)

&#x1f308;欢迎来到C专栏~~智能指针 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&…...

使用docker配置mysql主从复制

1.新建主服务器容器实例&#xff1a; docker run -p 3307:3306 --name mysql \ -v /docker/mysql/data:/var/lib/mysql \ -v /docker/mysql/conf:/etc/mysql/conf \ -v /docker/mysql/log:/var/log/mysql \ -e MYSQL_ROOT_PASSWORDroot \ -d mysql:5.7 设置容器卷之后&#xf…...

v3 异步组件及分包使用

1 app.vue <template> <!-- vue3异步组件必须使用suspense --> <Suspense> <template #default> <!-- 异步组件 --> <SyncVue></SyncVue> </template> <template v-slot:fallback> <!-- 优先显示骨架屏 --> <…...

实用调试技巧【上篇】

&#x1f534;本文章是在 Visual Studio 2022&#xff08;VS2022&#xff09;编译环境下进行操作讲解 文章目录&#x1f973;1. 什么是bug&#xff1f;&#x1f973;2.调试有多重要&#xff1f;2.1. 我们是如何写代码的&#xff1f;2.2.调试是什么&#xff1f;2.3.调试的基本步…...

JavaScript 教程

手册简介JavaScript 是世界上最流行的脚本语言。 JavaScript 是属于 web 的语言&#xff0c;它适用于 PC、笔记本电脑、平板电脑和移动电话。 JavaScript 被设计为向 HTML 页面增加交互性。 许多 HTML 开发者都不是程序员&#xff0c;但是 JavaScript 却拥有非常简单的语法。几…...

在SpringBoot里面使用原生的Servlet

在SpringBoot里面使用Servlet 首先在主程序中添加注解主程序添加ServletComponentScan // 加上这个注解之后就可以使用原生的组件了 HttpServlet 继承HttpServlet 重写方法 添加WebServlet 第一种方式使用注解 WebServlet(value "/helsk") public class HelloSe…...

商标被驳回,先别慌!挽回商标有办法

商标注册是一个漫长的等待过程&#xff0c;提交了注册申请之后不代表就能得心应手。商标局在接收到申请后&#xff0c;便会开始各阶段审查&#xff0c;面对不符合条件的商标会予以商标驳回。商标局基于什么原因而驳回注册申请呢?驳回后还有必要进行商标驳回复审吗?今天心周企…...

VMware安装Linux虚拟机后忘记root密码处理方法

OS版本&#xff1a;Red Hat 7.7 问题说明&#xff1a; 之前用VMWare安装了一台Linux虚机&#xff0c;由于长期没使用&#xff0c;导致忘记了root密码。所以需要修改root密码。 Root密码修改 现将修改root密码的操作步骤记录如下。 1.启动虚拟机&#xff0c;出现启动倒计时…...

Centos安装OpenResty

文章目录一. OpenResty是什么二. OpenResty的安装1. 安装开发库2. 安装OpenResty仓库3. 安装OpenResty4. 安装opm工具5. 目录结构6. 配置nginx的环境变量7. 启动和运行8. 配置文件修改三. 小案例1. 案例说明2. OpenResty监听请求3. 编写业务代码4. 获取请求参数一. OpenResty是…...

阿里云部署SpringBoot项目

目录 步骤1&#xff1a;购买服务器(新用户免费试用一个月) 步骤2&#xff1a;查看服务器相关信息 ​编辑 步骤3&#xff1a;设置安全组 步骤4&#xff1a;远程连接 步骤5&#xff1a;使用FinalShell连接阿里云服务器 步骤6&#xff1a;阿里云服务器上安装JDK ​编辑 步骤7…...

EdgeCOM嵌入式边缘计算机的参数配置

EdgeCOM嵌入式边缘计算机的参数配置&#xff1a; 下面以 eth0 为例进行命令说明。 在 Linux 系统下&#xff0c;使用 ifconfig 命令可以显示或配置网络设备&#xff0c;使用 ethtool 查询及 设置网卡参数。 设置 IP 地址&#xff0c;查看当前网卡详情&#xff1a; rootfl-imx6u…...

字节软件测试岗:惨不忍睹的三面,幸好做足了准备,月薪15k,拿到offer

我今年25岁&#xff0c;专业是电子信息工程本科&#xff0c;19年年末的时候去面试&#xff0c;统一投了测试的岗位&#xff0c;软件硬件都有&#xff0c;那时候面试的两家公司都是做培训的&#xff0c;当初没啥钱&#xff0c;他们以面试为谎言再推荐去培训这点让我特别难受。 …...

【编程基础之Python】5、安装Python第三方模块

【编程基础之Python】5、安装Python第三方模块安装Python第三方模块为什么需要安装第三方模块Python包管理器介绍pippip installpython -m pip installcondaconda install在Windows环境中安装Python模块安装numpy安装pandas安装matplotlib在Linux环境中安装Python模块在PyCharm…...

JavaScript 教程导读

JavaScript 是 Web 的编程语言。所有现代的 HTML 页面都使用 JavaScript&#xff0c;可以用于改进设计、验证表单、检测浏览器、创建cookies等。JavaScript 非常容易学。本教程将教你学习从初级到高级JavaScript知识。JavaScript 在线实例本教程包含了大量的 JavaScript 实例&a…...

BigDecimal

文章目录1. BigDecimal 的舍入模式&#xff08;RoundingMode&#xff09;1.1 ROUND_UP1.2 ROUND_DOWN1.3 ROUND_HALF_UP1.4 ROUND_HALF_DOWN1.5 ROUND_CEILING1.6 ROUND_FLOOR1.7 ROUND_HALF_EVEN1.8 ROUND_UNNECESSARY2. BigDecimal的运算——加减乘除2.1 加法 add()函数 减法…...

代码随想录【Day15】|102. 二叉树的层序遍历、226. 翻转二叉树、101. 对称二叉树

102. 二叉树的层序遍历 题目链接 题目描述&#xff1a; 给你一个二叉树&#xff0c;请你返回其按 层序遍历 得到的节点值。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 难点&#xff1a; 思路&#xff1a; 需要借用一个辅助数据结构即队列来实现…...

Python学习笔记:快速上手:基础知识

快速上手&#xff1a;基础知识 数和表达式 除法 >>> 1 / 2 0.5 >>> 1 / 1 1.0整除 >>> 1 // 2 0 >>> 1 // 1 1 >>> 5.0 // 2.4 2.0求余&#xff08;求模&#xff09;&#xff1a; x % y 等价于x - ((x // y) * y)。 …...

excel学习笔记-导入外部文件,报错,数值格式变换,日期格式的转化,求和快捷键,冻结窗格

这里写目录标题一、导入外部文件1.导入csv文件2.导入txt文件3.修改txt内容&#xff0c;需要刷新才能看见更改二、报错三、数值格式变换四、日期格式的转化五、ALT &#xff0c;求和快捷键六、冻结窗格一、导入外部文件 1.导入csv文件 2.导入txt文件 3.修改txt内容&#xff0c;…...

06 OpenCV‘阈值处理、自适应处理与ostu方法

1 基本概念 CV2中使用阈值的作用是将灰度图像二值化&#xff0c;即将灰度图像的像素值根据一个设定的阈值分成黑白两部分。阈值处理可以用于图像分割、去除噪声、增强图像对比度等多个领域。例如&#xff0c;在物体检测和跟踪中&#xff0c;可以通过对图像进行阈值处理来提取目…...

月薪过万的那些人,大部分都是做什么工作的?

三百六十行&#xff0c;行行出状元。不管是什么行业&#xff0c;月薪过万都是有的。只不过有些行业就是比较容易出现月薪过万&#xff0c;换句话说&#xff0c;就是这个行业内出现月薪过万的人数比较多。先说结论&#xff0c;综合来看月薪过万的这部分90后&#xff0c;大部分集…...

csgo搬砖项目,门槛最低的副业就是它(内附入门知识及选品技巧)

CSGO搬砖如何选择游戏饰品(装备&#xff09;&#xff1f;相信很多朋友一定很关心这个问题&#xff0c;因为如何选品直接关系到该装备是否快速出售&#xff0c;而且也关系到账号整体盈收状况。那么今天阿阳就来好好聊聊如何选择Steam装备以及饰品的各项知识点。 Steam搬砖如何选…...

【闲聊杂谈】高并发下基于LVS的负载均衡

1、使用http协议进行网络请求 在前几年公布的用户入网数据中&#xff0c;移动入网的数量已经达到六七亿的规模&#xff0c;固网用户数也达到三至五个亿。想要解决这么大并发访问的场景&#xff0c;有多种的解决方案&#xff0c;常规有基于4层的&#xff0c;也有基于7层的。这个…...

Redis新数据类型

目录 Bitmaps 简介 命令 Bitmaps和set对比 HyperLogLog 介绍 命令 Geospatial 简介 命令 Bitmaps 简介 现代计算机用二进制(位)作为信息的基本单位&#xff0c;1个字节等于8位。合理的使用和操作位可以有效的提高内存的使用率和开发效率。 redis提供了Bitmaps这个"数据类…...

使用Python绘制股票CCI指标曲线

本文使用Python语言绘制一只股票的CCI&#xff08;Commodity channel index&#xff09;曲线&#xff0c;论文参考《Commodity channel index: Tool for trading cyclic trends》&#xff0c;该指标可以用来测量股价、外汇或者贵金属交易是否已超出常态分布范围&#xff0c;​ …...

【C语言技能树】浮点数在内存中的存储

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

Spring框架源码(五) @configuration源码深度解析

Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解&#xff0c;开发者可以通过Configuration注解来定义配置类&#xff0c;也可以使用xml形式注入。 例如配置数据库配置&#xff0c;定义一个配置类&#xff0c;注入数据源DataSource, 事务管理器Trans…...

gcc/g++从入门到精通(3)gcc头文件、库搜索路径方式全面盘点

🎀 关于博主👇🏻👇🏻👇🏻 🥇 作者简介: 热衷于知识探索和分享的技术博主。 💂 csdn主页::【奇妙之二进制】 ✍️ 微信公众号:【Linux 世界】 🎉精彩专栏: 🎓 【面向工作git基础教程】 ​ 🧡 【C++11新特性深入剖析】 ​ 📚【shell脚本编程基础与...

Android Studio多渠道打包及自动化构建

Android 有不同的应用市场&#xff0c;也就是不同的渠道&#xff0c;需要为每个应用市场打一个安装包&#xff0c;但主要的代码是一样的&#xff0c;可能部分资源不一样&#xff0c;部分代码不一样&#xff0c;如果每个渠道都需要修改&#xff0c;然后打包&#xff0c;非常耗时…...

基于MATLAB的MIMO信道估计(附完整代码与分析)

目录 一. 介绍 二. MATLAB代码 三. 运行结果与分析 一. 介绍 本篇将在MATLAB的仿真环境中对比MIMO几种常见的信道估计方法的性能。 有关MIMO的介绍可看转至此篇博客&#xff1a; MIMO系统模型构建_唠嗑&#xff01;的博客-CSDN博客 在所有无线通信中&#xff0c;信号通过…...