astore

深入理解JVM

旧街凉风 提交于 2020-02-25 22:56:04
方法调用并不等同于方法中的代码被执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还未涉及方法内部的具体运行过程。一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(也就是之前说的直接引用)。 解析 所有方法调用的目标方法在Class文件里面都是一个常量池中的符号引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用,这种解析能够成立的前提是:方法在程序真正运行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好、编译器进行编译那一刻就已经确定下来。这类方法的调用被称为解析(Resolution),在Java语言中符合这种要求的主要有 静态方法 和 私有方法 。 方法调用指令 invokestatic :用于调用静态方法。 invokespecial :用于调用实例构造器<init>()方法、私有方法和父类中的方法。 invokevirtual :用于调用所有的虚方法。 invokeinterface :用于调用接口方法,会在运行时再确定一个实现该接口的对象。 invokedynamic :先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。 前面4条调用指令,分派逻辑都固化在Java虚拟机内部,而 invokedynamic

浅谈JVM

ⅰ亾dé卋堺 提交于 2019-12-25 17:32:41
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 1.1 定义 Program Counter Register 程序计数器(寄存器) 定义:当前线程所执行的字节码的行号指示器,记住下一条执行 JVM 指令的地址 特点 线程私有的 多个线程运行同一套代码时,CPU会有一个调度器组件为不同线程分配时间片,在时间片内线程未执行完成就会将运行状态暂存,再切换到其他线程执行,如此循环执行。而程序计数器就是线程运行状态暂存的区域,在线程切换时,会记录该线程的下一条JVM指令的地址。 JVM 规范中唯一一个不会存在内存溢出的区域 1.2 作用 示例代码 0: getstatic #20 // PrintStream out = System.out; 3: astore_1 // -- 4: aload_1 // out.println(1); 5: iconst_1 // -- 6: invokevirtual #26 // -- 9: aload_1 // out.println(2); 10: iconst_2 // -- 11: invokevirtual #26 // -- 14: aload_1 // out.println(3); 15: iconst_3 // -- 16: invokevirtual #26 // -- 19: aload_1 //

不能用 + 拼接字符串? 这次我要吊打面试官!

北城余情 提交于 2019-12-23 09:56:58
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 好久没维护《 吊打面试官 》系列了,今天再来一篇,这次真的要吊打了,哈哈!(看往期吊打系列请在后台回复: 吊打 ,我会陆续更新……) 我们做 Java 程序员以来,不管是工作当中,还是面试过程中,都知道:字符串拼接不能用 String,要用 StringBuilder 或者是 StringBuffer ,以至于它们都被滥用了。 StringBuilder、StringBuffer 简称:SB,下文统一用 SB 代替。 SB 它们都是可变的字符串,它们之间的区别也是 Java 初中级面试战场上出现几率十分高的一道题,上场率没有 90% 也有 80% 吧。 这两个的具体区别请看这篇文章: StringBuffer 和 StringBuilder 的 3 个区别 。 我们反过来想下,String真的是不可变的么?不一定,看下这篇: Java 中的 String 真的是不可变的吗? 当然,本文不是讨论字符串可变与不可变的问题,而是讨论:字符串拼接一定要用 SB 吗?为什么不能用 + ?能不能用 + ?什么时候可以用 + ? 为什么不能用 + 号拼接字符串?我不服,接下来我要吊打面试官! 什么时候不能用 + 通过多个表达式完成一个字符串拼接操作。 private void test1() { String www =

Class文件格式

▼魔方 西西 提交于 2019-12-09 11:55:34
 我们知道Java是一门跨平台的语言,我们编写的Java代码会被编译成中间class文件以让Java虚拟机解析运行。而Java虚拟机规范仅仅描述了抽象的Java虚拟机,在实现具体的Java虚拟机时,仅指出了设计规范。Java虚拟机的实现必须体现规范中的内容,但仅在确有必要时才应该受制于这些规范。对于完整内容,可以查看原文档,以JDK7为例,可查看 https://docs.oracle.com/javase/specs/jvms/se7/html/ ,或者《深入理解Java虚拟机 JVM高级特性与最佳实践》一书。完整的规范主要包含以下内容: 第2章:概览Java虚拟机整体架构 第3章:介绍如何将Java语言编写的程序转换为虚拟机指令集 第4章:定义class文件格式。它是一种与硬件和操作系统无关的二进制格式,用来表示编译后的类和接口 第5章:定义了Java虚拟机启动以及类和接口的加载、链接和初始化的过程 第6章:定义了Java虚拟机指令集 第7章:提供了一张以操作码值为索引的Java虚拟机操作码助记表  本文只是大概记录项目需要了解的基础概念,着重在介绍Class文件格式上,为该系列后续内容做铺垫。  Class文件是一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在class文件中,中间没有任何分割符。每个 Class 文件都是由 8 字节为单位的字节流组成

Class文件格式

99封情书 提交于 2019-12-08 17:14:40
 我们知道Java是一门跨平台的语言,我们编写的Java代码会被编译成中间class文件以让Java虚拟机解析运行。而Java虚拟机规范仅仅描述了抽象的Java虚拟机,在实现具体的Java虚拟机时,仅指出了设计规范。Java虚拟机的实现必须体现规范中的内容,但仅在确有必要时才应该受制于这些规范。对于完整内容,可以查看原文档,以JDK7为例,可查看 https://docs.oracle.com/javase/specs/jvms/se7/html/ ,或者《深入理解Java虚拟机 JVM高级特性与最佳实践》一书。完整的规范主要包含以下内容: 第2章:概览Java虚拟机整体架构 第3章:介绍如何将Java语言编写的程序转换为虚拟机指令集 第4章:定义class文件格式。它是一种与硬件和操作系统无关的二进制格式,用来表示编译后的类和接口 第5章:定义了Java虚拟机启动以及类和接口的加载、链接和初始化的过程 第6章:定义了Java虚拟机指令集 第7章:提供了一张以操作码值为索引的Java虚拟机操作码助记表  本文只是大概记录项目需要了解的基础概念,着重在介绍Class文件格式上,为该系列后续内容做铺垫。  Class文件是一组以8字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑排列在class文件中,中间没有任何分割符。每个 Class 文件都是由 8 字节为单位的字节流组成

深入分析String.intern和String常量的实现原理

瘦欲@ 提交于 2019-12-05 05:46:59
背景 字符串类型在实际应用场景中使用非常频繁,如果为每个字符串常量都生成一个对应的String对象,明显会造成内存的浪费,针对这一问题,虚拟机实现一个字符串常量池的概念,提供了如下实现: 1、同一个字符串常量,在常量池只有一份副本; 2、通过双引号声明的字符串,直接保存在常量池中; 3、如果是String对象,可以通过String.intern方法,把字符串常量保存到常量池中; 本文JVM源码版本 openjdk-7-fcs-src-b147-27 疑惑 在不同环境执行上述代码,会得到不同的结果,为什么? 1、JDK1.6的结果: false false 2、JDK1.7的结果: true false 解惑 其中 String.intern 在java中是native方法,JDK1.7的注释如下: 1、执行intern方法时,如果常量池中存在和String对象相同的字符串,则返回常量池中对应字符串的引用; 2、如果常量池中不存在对应的字符串,则添加该字符串到常量中,并返回字符串引用; HotSpot1.6实现 常量池的内存在永久代进行分配,永久代和Java堆的内存是物理隔离的,执行intern方法时,如果常量池不存在该字符串,虚拟机会在常量池中复制该字符串,并返回引用,使用intern方法时需要谨慎,避免常量池中字符串过多,导致性能变慢,甚至发生PermGen内存溢出。 显然 s

函数嵌套调用性能好还是函数分开写性能好?

倾然丶 夕夏残阳落幕 提交于 2019-11-30 07:11:12
函数嵌套调用性能好还是分开写性能好? 下午突然想到一个问题: * 形式如下的两种方法,哪一种的效率更高一点? 第一种: A=fun1(args1[]); B=fun2(A,args2[]); C.fun3(B); 1 2 3 第二种: C.fun3(fun2(fun1(args1[]),args2[])); 1 也就是说,一段结果相同的代码,是将中间使用的函数嵌套起来写性能更好还是分开写性能更高呢? 这里假定变量不会再被后面的代码使用,不存在复用的问题,同时,也将可读性问题暂放一边,仅仅从性能角度考虑。 直觉上,应当是第二种形式的性能更高一些,减少了中间变量存储,但是我还是有一点迷惑:函数调用时系统要保存现场,对各种变量在栈中进行保存,调用完了还要恢复现场,恢复各种上一个方法中的值,第二种形式是不是在这方面消耗了更多性能呢?毕竟在第二种形式中,刚想调用fun3,发现还要调用fun2,刚想调用fun2,发现还必须先调用fun1。 本着先敲一敲看看的想法,我用java写了一个测试代码(放到文章最后面),从javap反编译的中间代码上能看出上面两个问题的答案: 第一种形式: 第二种形式: 从代码上,可以看出,第一种形式的确会多保存两个临时变量,多了成对的两个命令’astore’、’aload’从寄存器中存读变量。 而第二种形式在编译成中间代码之后,函数的调用顺序也变成了fun1-

Java虚拟机的指令

放肆的年华 提交于 2019-11-29 07:07:11
Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需参数(称为操作数,Operands)而构成。 基本数据类型 1、除了long和double类型外,每个变量都占局部变量区中的一个变量槽(slot),而long及double会占用两个连续的变量槽。 2、大多数对于boolean、byte、short和char类型数据的操作,都使用相应的int类型作为运算类型。 加载和存储指令 1、将一个局部变量加载到操作栈:iload、iload_<n>、lload、lload_<n>、fload、fload_<n>、dload、dload_<n>、aload、aload_<n>。 2、将一个数值从操作数栈存储到局部变量表:istore、istore_<n>、lstore、lstore_<n>、fstore、fstore_<n>、dstore、dstore_<n>、astore、astore_<n>。 3、将一个常量加载到操作数栈:bipush、sipush、ldc、ldc_w、ldc2_w、aconst_null、iconst_m1、iconst_<i>、lconst_<l>、fconst_<f>、dconst_<d>。 4、扩充局部变量表的访问索引的指令:wide。 _<n>:_0、_1、_2、_3,

Java 自动拆箱和自动装箱学习笔记

我是研究僧i 提交于 2019-11-28 19:31:22
Java 自动拆箱和自动装箱学习笔记 详情参考以下 深入剖析Java中的装箱和拆箱 JDK自动拆箱下,三目运算符的潜规则 Java 自动装箱与拆箱的实现原理 Autoboxing and Unboxing 1. 概述 Java 中的自动装箱和自动拆箱算是一种语法糖,也就是在编译阶段编译器在合适的情况下帮我们的做了自动拆箱和自动装箱。 众所周知,Java 中的基本数据类型并不是对象,为了解决在一定切情况下我们需要使用对象的时候,Java 为我们提供了每个基本类型对应的包装类,如下表: 基本数据类型 数据类型包装类 byte(1字节) Byte short(2个字节) Short int(4个字节) Integer long(8个字节) Long float(4个字节) Float double(8个字节) Double boolean Boolean 当基本数据类型转换成数据类型包装类时,称之为 装箱 ; 当数据类型包装类转换成基本数据类型时,称之为 拆箱 。 Integer i = 10; // 装箱 int num = i; // 拆箱 2. 自动拆箱和自动装箱的实现原理。 对于上面一段代码进行反编译可以得到: 0: bipush 10 2: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang

击穿JVM虚拟机

你离开我真会死。 提交于 2019-11-25 23:17:40
什么是JVM虚拟机 首先我们需要了解什么是虚拟机,为什么虚拟机可以实现夸平台,虚拟机在计算机中扮演一个什么样的角色。 (从下向上看) 看上图的操作系统与虚拟机层,可以看到,JVM是在操作系统之上的。他帮我们解决了操作系统差异性操作问题,所以可以帮我们实现夸操作系统。 JVM是如果实现夸操作系统的呢? 接着向上看,来到虚拟机可解析执行文件这里,虚拟机就是根据这个.class的规范来实现夸平台的。 在向上到语言层,不同的语言可以有自己的语法、实现方式,但最终都要编译为一个满足.class规范的文件,来让虚拟机执行。 所以理论上,任何语言想使用JVM虚拟机实现夸平台的操作,都可以根据规范生成.class文件,就可以使用JVM,并实现“一次编译,多次运行”。 虚拟机具体帮我们都做了哪些工作? 字节码规范(.class) 内存管理 第一点已经在上边说过,不在重复。 第二点内存管理也是我们接下来主要讲的内容。在没有JVM的时代,在C/C++时期,写代码中除了写正常的业务代码之外,有很大一部分代码是内存分配与销毁相关的代码。稍有不慎就会造成内存泄露。而使用虚拟机之后关于内存的分配、销毁操作就都由虚拟机来管理了。 相对的肯定会造成虚拟机占用更多内存,在性能上与C/C++对比会较差,但随着虚拟机的慢慢成熟性能差距正在缩小。 JVM架构 Jvm虚拟机主要分为五大模块:类装载子系统、运行时数据区