![](https://img-blog.csdnimg.cn/20201014180756724.png?x-oss-process=image/resize,m_fixed,h_64,w_64)
JVM
文章平均质量分 89
大力海棠
蓝桥杯Java组个人赛省二,麻瓜ACMer的北京尚学堂·百战程序员( ̄y▽ ̄)~*
展开
-
栈上替换、方法内联和代码缓存
目录OSR栈上替换方法内联代码缓存大小 上一篇日志讲到使用JIT即使编译方式下代码的执行方式有解释执行和编译执行两种,对于程序中函数的执行,要么是使用解释执行的方式,要么是使用编译执行方式(对于热点代码)编译后得到的机器码执行,这两种方式的切换发生在函数的两次调用间隔,前一次调用时虚拟机没有把这段函数判定为热点代码,后一次调用时发现其是热点代码并且进行了编译,那么就会使用提供的机器码执行。对于大部分的函数执行来说是这样的,但有一些情况例外,如调用次数不多且里面有很多循环操作的函数,...原创 2021-03-01 17:45:23 · 1129 阅读 · 2 评论 -
虚拟机执行效率优化:JIT相关参数
目录三种执行模式编译阈值多级编译 我们知道Java程序经过编译得到字节码文件后,再通过解释器解释执行,相对于其他静态编译执行语言(也就是程序在执行前全部被翻译成机器码),解释执行的效率要低很多。原因简单来说是字节码解释器在执行时使用的软件代码来模拟字节码执行,如果是使用模板解释器来执行,每一条字节码关联一个模板函数,模板函数里面就是字节码对应的机器指令,性能有所提升,这些是对解释器的优化。但还不够,相信大家都听过Java虚拟机中的JIT即时编译技术,其工作过程是一边翻译一边执行,将...原创 2021-02-22 17:38:53 · 840 阅读 · 0 评论 -
javac静态编译优化总结
目录计算表达式优化字符串连接条件语句优化switch语句优化 我们都知道Java有自己的语言规范,JVM也有自己的解析规则,Java程序能够运行在虚拟机上,得益于javac编译器,将Java语言编译成字节码,javac属于静态编译,因为它不能直接把程序源码编译成机器可以识别的机器码,javac将程序整个完全编译成字节码后,才能交由虚拟机解释执行,这个过程需要耗费的时间(对比于其他可一边编译一边执行的语言)和空间(整个完全编译完成)都比较大。为了提高程序的执行速度,有的虚拟机里提...原创 2021-02-15 17:54:55 · 873 阅读 · 0 评论 -
JVM常用指令:比较控制和函数调用
目录比较控制比较指令条件跳转分支跳转函数调用和返回 程序的顺序执行需要由条件控制指令来控制,例如条件跳转、比较和分支跳转等,JVM常用指令中有一类控制指令,负责虚拟机有条件地或者无条件地从指定位置执行指令,使得程序按照我们希望的顺序执行。总的来说控制指令分为比较、跳转、比较跳转和有/无条件的分支跳转,同样控制程序执行的还有函数调用和返回指令,这篇日志就来总结这两部分常用的指令。比较控制比较指令 比较指令有fcmpg、fcmpl、dcmpg、dcm...原创 2021-02-08 17:48:45 · 349 阅读 · 0 评论 -
JVM常用指令:对象操作、运算和类型转换
目录类型转换指令基本运算指令对象操作指令 上一篇接照按指令的功能和操作的数据类型进行分类,除了压栈出栈指令外,还有类型转换,运算和对象操作指令三种,即加减乘除取余取反,对象操作中的字段访问和类型检查等,这篇日志就来总结下这部分常用的JVM指令。类型转换指令 类型转换在代码中出现一般是用户进行显式的数据类型转换操作,例如(int)num;在指令集中助记符形式是x2y,表示将x类型数据转换成y类型数据,例如i2l表示将int数据类型转换成long数据类型,l2d...原创 2021-02-01 18:00:13 · 360 阅读 · 0 评论 -
JVM常用指令:常量,变量的压栈出栈指令
目录常量压栈指令局部变量表压栈指令操作数栈出栈指令通用指令 上一篇日志里用到的指令如bipush,iload等都是JVM常用的指令,它们有各自的分类,如bipush是常量压入操作数栈,iload是将数组指令索引压入操作数栈,根据操作的数据类型不同还可以分为,i开头的是对integer类型操作指令,f开头是对float类型操作指令。每一条字节码指令由一个字节的助记符加上后面的参数组成,例如sipush 100指令一共占用3个字节,sipush占一字节加上后面的参数占两个字节。这...原创 2021-01-25 17:46:00 · 846 阅读 · 0 评论 -
反编译java文件看字节码执行顺序
目录反编译信息字节码执行入栈出栈退出临界区 上一篇日志开头里讲到,.java文件加载前首先被Java编译器编译成.class文件,也就是字节码文件,类似于C程序编译链接后形成的可执行文件,但是.class文件里存放的不是操作系统能直接执行的指令,而是JVM能识别的指令,它需要装载到JVM里解释运行。.class文件里的结构很复杂,保存的二进制流是一些如class的一些接口,方法列表,常量池,版本号,魔数等,这篇日志用一段简单的代码通过对其反编译来看看里面的每一句话被转化成了什么...原创 2021-01-18 17:57:23 · 498 阅读 · 1 评论 -
自定义ClassLoader实现类的热加载
目录热加载逻辑自定义ClassLoader热加载类类加载过程 热加载简单来说就是在程序运行时可以重新加载之前经过编译转换后的类,Java并不支持热加载,因为我们编写的代码文件,也就是.java文件在加载前首先被Java编译器编译成.class文件,当程序执行需要使用到这个类时,会将它的.class文件加载到JVM里,通过类加载器读取这些.class文件后,转化成类实例,即可以生成类的对象。对于同一个类来说,JVM只会把它加载一次,加载完成后也不能把它删除掉,如果我们想要在运行过...原创 2021-01-11 17:49:45 · 805 阅读 · 0 评论 -
从工厂模式回看解决双亲委派问题
目录上下文加载器突破双亲委派模式JDK 9双亲委派模型 简单工厂模式下,假设核心类和工厂方法由启动类加载器加载,应用类由应用类加载器加载,因为双亲委派模式下上层的类加载器无法访问下层ClassLoader加载的类,所以会导致启动类加载器加载的工厂方法无法创建应用类加载器加载的类的实例。在工厂模式中解决的方式就是把工厂方法抽象出来,具体生成实例的逻辑交由子类来实现,简单工厂模式变成抽象工厂模式,这个在上一篇日志中总结到,那么回到双亲委派问题,JVM解决的方式是在启动类加载器代码中新建...原创 2021-01-04 17:49:52 · 211 阅读 · 0 评论 -
从ClassLoader双亲委派看工厂模式
目录双亲委派模式问题简单工厂模式抽象工厂模式示例双亲委派模式问题 双亲委派模式在类加载时会先交由其双亲加载器来处理,如果请求失败了,再由自己加载,这样做其中一点是出于安全考虑。检查类有没有被加载前面的日志里总结过,是从最底层自定义类加载器开始往上检查(加载类则是从顶到往下加载),最后到启动类加载器,这里有一点要注意的是,底层的类可以访问其双亲类加载器加载的类,反过来就不行,顶层的类无法访问自己下层加载的类,这样就会出现一些问题,我们知道启动类加载器负责加载系统中一些核心类,...原创 2020-12-28 17:47:31 · 124 阅读 · 0 评论 -
ClassLoader分类和双亲委派模式
目录ClassLoader分类核心类双亲委派模式加载示例 加载类的过程需要从系统外部根据类名获取其二进制数据流,途径可以是外部文件,数据库或者从网络上download,接着把它们交给JVM,让虚拟机将这些数据转化成Class实例,这一过程通过ClassLoader类加载器来完成,ClassLoader只作用在类加载过程,获取二进制流交给JVM之后,接下来的连接和初始化等操作就不再参与了,对于ClassLoader,它是一个类,里面提供了一些方法来完成加载流程,例如publi...原创 2020-12-21 17:46:12 · 333 阅读 · 0 评论 -
Class字节码文件装载:类验证、解析和初始化
目录验证类解析类示例初始化不可逆的初始化过程 紧跟上一篇,Java程序在开始运行时并非完全加载的,各个部分在需要时才会加载,Class使用时被装载进JVM,类加载器ClassLoader会检查这个Class对象是否已经加载,如果尚未加载,ClassLoader就会根据类名查找.class文件,加载过程获取得到二进制数据流并解析成数据结构后,下一步就进入连接阶段,连接时进行的一步很重要的操作是验证,包括格式、语义、字节码和引用等验证,保证加载进来的Class字节码是符合规范的...原创 2020-12-14 17:43:59 · 369 阅读 · 0 评论 -
JVM中的锁(下):粒度、分离和锁粗化
目录锁分离锁粗化减少锁占有时间减少锁粒度 上两篇日志从锁的实现方面,总结了JVM对锁的一些实现和使用中膨胀的过程,从偏向锁、轻量级锁到自旋再到重量级锁,随着线程竞争越来越激烈,锁膨胀的也越来越厉害,不同锁的实现在不同场景下有它的优点和缺点,没错,在某些场景下,锁操作并不一定总能优化程序的,例如偏向锁在线程竞争激烈的场景下,需要不断变换自己的偏向状态,自旋锁在线程占用锁时间较长的场景下做的自旋操作通常是无用功,最后还是得不到锁,浪费了占用的CPU。所以,为了让程序执行的更加连贯,...原创 2020-11-30 17:45:46 · 347 阅读 · 0 评论 -
JVM中的锁(中):锁膨胀和锁消除
目录锁膨胀自旋锁重量级锁锁消除 接着上一篇,偏向锁失效后JVM让线程去申请轻量级锁,轻量级锁就是一种乐观思想,举个例子,在写入数据时会先判断这个期间有没有其他线程修改过这个数据,具体方式是读出版本号or时间戳,如果没有,就可以加锁,进行写入修改,否则继续重复读出数据,比较,再写入,轻量级锁就是这么个思想,使用的也是CAS操作。如果轻量级锁获取失败,证明有多个线程在同时竞争这个对象,这时候临界区线程竞争激烈,如果想要保证数据完整性,就要采用更重量级别的锁,从偏向锁到轻量级锁,再到...原创 2020-11-23 17:49:05 · 781 阅读 · 0 评论 -
JVM中的锁(上):对象头和锁
目录Java对象头Mark WordJVM中的锁偏向锁轻量级锁 以前的日志里总结过Java中锁的应用,在多线程编程专栏里,JVM内部对锁进行了一些优化,所以在Java程序中使用锁很方便,接下来的日志就总结下锁在虚拟机里的实现和优化。锁的作用简单来说是为了保护多线程访问同一内容,也就是临界区内容时,因多个线程同时读,写入操作导致数据一致性出现问题,为了保证事务执行前和执行后数据的完整性没有遭到破坏,可以通过给对象加锁来让多线程按顺序逐一访问临界区,这样大家访问到的资源总是正确...原创 2020-11-16 17:41:33 · 845 阅读 · 0 评论 -
JVM性能:堆的参数配置
目录初始堆和最大堆偏差问题新生代配置指定绝对大小 Java程序中的最重要的内存空间就是堆了,基本上所有的对象都分配在堆中,这些堆空间根据不同功能和GC回收分配成几种的结构,例如存放新创建对象的新生代,经过多次GC后仍然存活的老年对象则存放在老年代中,新生代区域又分为eden区和from区,to区,通风常在eden区中的新生代经历了一次GC后仍然存活的话,便会进入from区或to区,每一次GC都会让对象的年龄增加1,达到进入老年对象的值后,便会转入老年代中。如果堆空间的使用不恰当...原创 2020-11-09 17:47:49 · 800 阅读 · 0 评论 -
String字符串导致的JVM内存泄漏
目录被定义为final类不可改变常量池优化String造成的内存泄漏String结构 String类存在于java.lang包中,在程序里使用挺广泛的,用来创建一个字符串对象变量,Java内部对String类做了一些特殊的处理,例如把String类声明成final类型,也就是说不能有子类,String类型对象一旦创建后就不可改变(你可能会想不是可以拼接字符串吗,怎么不可以改变String类对象了,别急,下面慢慢看),以及一些针对JVM的优化等,先来简单看看String类在J...原创 2020-11-02 17:58:17 · 2209 阅读 · 0 评论 -
总结Java程序内存溢出原因
目录内存溢出和内存泄漏直接内存溢出堆溢出方法区溢出 这篇日志总结下Java程序中的发生内存溢出的一些原因,我们知道JVM堆空间十分重要,大部分对象在创建时都是放在堆中(除了一些逃逸对象是栈上分配),例如新生代存放在eden区中。随着对象的不断创建和老年代对象的不断产生,如果垃圾回收不能及时释放内存,最终堆内存被耗尽,新对象创建时由于内存不足,申请空间失败,导致内存溢出,在Java程序中内存溢出分几种,直接内存溢出,堆溢出和永久区溢出,在总结这几种内存溢出情况前,先分清内存溢出和...原创 2020-10-26 17:45:04 · 1390 阅读 · 0 评论 -
在TLAB(线程本地分配缓存)上分配对象
目录使用TLAB性能差异示例分配策略Java对象分配过程 JVM的Thread Local Allocation Buffer,即TLAB线程本地分配缓存,作用是加速分配对象空间,以前的日志里说到对于一些线程私有的对象,由于不会发生对象逃逸,所以JVM会以栈上分配的方式将对象空间分配到栈空间中,这样当方法调用完成后对象就会因离开了作用域而跟着一起被销毁,不需要等待GC来回收,而对于一些会发生逃逸的对象,例如类的成员变量,因为它们可能被其他线程访问,所以JVM会将其分配在堆空间中...原创 2020-10-19 17:48:33 · 606 阅读 · 0 评论 -
总结JVM默认回收器-G1回收器
目录串行/并行回收器G1回收器并发标记回收混合回收串行/并行回收器 这篇日志把JVM使用的G1回收器总结一下,在JVM支持的垃圾回收器中,有串行和并行回收器,串行回收器就是使用单一线程来进行垃圾回收,例如新生代串行回收器在进行垃圾回收时会停止整个Java程序的运行,因为单线程没有线程之间的切换,而且使用的是使用的复制算法,只需把可回收对象复制到其他空间,然后清除原来空间即可,整个回收过程效率较高(新生代区中因为可达的对象较少,可回收对象较多,所以使用复制算法将较少的存活对象...原创 2020-10-12 17:47:25 · 382 阅读 · 0 评论 -
可触及性(下):总结Java四种强度引用
目录非立即回收的软引用示例引用强度最弱的虚引用示例 接着上一篇,总结完强引用和弱引用的例子,强引用简单来说情况就是局部变量表中存在变量指向了堆空间中的对象实例,可以存在多个变量同时指向同一实例,如果用关系运算符“==”来判断这些对象得到的是true,因为它们都指向了相同的堆空间地址,此时对象实例因为存在强引用所以GC不会对其进行回收。弱引用在GC过程中,只要扫描个对象时发现存在弱引用对象,按道理来说会立刻对它进行回收,实际情况中因为JVM垃圾回收的线程优先级并不高,可能不一定做...原创 2020-10-05 17:52:18 · 181 阅读 · 0 评论 -
可触及性(上):总结Java四种强度引用
目录finalize()函数复活对象示例引用强度不会被GC回收的强引用不可达后被GC回收的弱引用 上一篇日志简单总结了三种JVM垃圾回收方式,无论是使用引用计数、还是标记压缩清除法等,在进行垃圾回收时,都要先知道哪些对象是需要回收的,哪些是“活的”对象哪些是垃圾对象,判断这些对象的状态,方法是从根节点开始访问每一个对象,如果对象可达,表示该对象目前正在被使用,存在对其的引用,如果某一对象不可达,表示该已经不再被使用了,程序中不存在对其的引用,通常这类对象就是可...原创 2020-09-28 17:46:05 · 194 阅读 · 0 评论 -
JVM内存垃圾回收:复制、标记回收和引用计数法
还记得以前自己写C程序时,需要手动为指针变量申请空间,最后回收不用的指针,像这样:Typedef struct LinkList *List;List *p;p = (List)malloc(sizeof(struct List));// ……free(p); 有时会忘记free释放掉不用的指针,有时是不确定何时需要释放空间,对于我自己来说虽然都是学习过程中写的一些简单算法,即使忘记释放空间,也不至于造成内存泄漏,但是对于大型程序,内存的申请和释放操作频繁,如果...原创 2020-09-21 17:49:01 · 332 阅读 · 0 评论 -
JVM函数调用:Java出入栈
目录局部变量表索引复用垃圾回收栈数据区栈上分配 线程作为系统运算调度的最小单位,在JVM中线程的行为体现就是函数调用,函数调用中数据的传递就是通过Java栈,Java栈顾名思义有着和数据结构中“栈”相似的属性,后进先出,出栈入栈,栈中保存的是栈帧,当JVM发生函数调用时,就会有一个栈帧被压入Java栈,当函数调用结束后,再从栈中弹出栈帧,当前正在执行的函数其对应的栈帧位于栈顶处,且保存有当前函数的局部变量表和栈数据区(保存一些中间结果等数据)。在函数返回,也就是有栈帧要从J...原创 2020-09-14 17:49:01 · 540 阅读 · 0 评论 -
JVM字节对齐和字段重排
目录字节对齐struct示例内存布局优化结构体内存空间Java字节对齐 上一篇日志最后说到,非静态类型变量的经过起始偏移量和5中类型偏移量计算后,在统计总的内存空间大小前,需要进行内存对齐补白操作,这样做的原因是性能带来了提升,上一篇举过例子,对于进行了补白对齐的数据,CPU进行访问可能只需要一个周期即可,反过来如果内存起始地址并不是数据字节的倍数,那么可能读取某一数据时需要分两步到三步,将不同内存地址的数据读取出来后,去除头和尾多余的字节拼凑出目标地址的结果出来。总的来说...原创 2020-09-07 17:58:54 · 825 阅读 · 0 评论 -
类变量解析:静态/非静态偏移量
目录偏移量静态变量偏移量非静态变量偏移量内存对齐 根据常量池中的信息,JVM先进行内存分配,构建出constantPoolOop对象,为其分配足够的空间来保存字节码文件信息,再进行解析工作。在加载某一个类时,从常量池中还原出类里面定义的变量和方法,将它们保存到内存中,具体在constantPoolOop对象中的tag数组(存放一些索引等)和data区,在保存数据后,根据ClassFileParser::parseClassFile()链路,一系列如魔数解析、父类、接口解析之后,...原创 2020-08-31 17:51:52 · 410 阅读 · 0 评论 -
总结JVM常量池解析
目录constantPoolOop初始化解析类元素解析方法元素解析字符串元素解析 JVM要解析常量池里的数据,就要先进行内存分配,为常量池划分出一块内存空间,分配的空间需要多大?从哪里划出一片内存,这些在前面的日志里有总结过,在分配链路里有专门的函数根据常量池里的元素数量计算出需要的大小length,为常量池constantPoolOop对象分配的headSize(对象头)+length大小内存区域位于JVM的永久区permanent区,是一片连续的区域,最后进行清零等内存...原创 2020-08-24 17:48:28 · 599 阅读 · 0 评论 -
描述一个Java类型:oop、klass和handle类
目录虚函数列表opp、klass和handle类型oop体系klass体系handle类 现在使用广泛的hotspot虚拟机里,使用opp-klass模型,分两部分来表示Java类和对象,oop也就是ordinary object pointer,普通对象指针,用来标识对象的实例,klass则是用来描述类,标识里面的变量和方法等。oop体系和klass体系中都有instance、method、constantMethod和methodData等结构,大家共同组合起来一起描述...原创 2020-08-17 17:44:10 · 1038 阅读 · 0 评论 -
JVM常量池:内存分配链路
目录内存分配内存大小内存空间布局初始化内存 上两篇日志把一个简单例子的Java源程序十六进制字节码文件简单分析,JVM对字节码文件的解析顺序大致是从魔数、版本号、常量池、父类、接口、变量再到方法,也就是在第三步时解析程序中的常量池信息。常量池前面说过,它里面记录了Java类中的全部变量和方法信息,包括成员变量,类变量,成员方法和类方法,还有类的构造方法,最后JVM会将常量池信息解析后存储在内存模型的常量区中。JVM对常量池进行解析的链路大致如下:ClassFileParse...原创 2020-08-10 17:48:10 · 293 阅读 · 0 评论 -
分析.class字节码文件(下):标识,字段和方法
目录标识access_flags访问权限this_class包名和类名super_classinterfaces_count字段field_info fields方法method_info methods 接着上一篇,在第50字节处数据有一个类型为u1=1的,即UTF-8编码的字符串变量a,在它之后又有一个UTF-8编码字符串“Ljava/lang/String”,表示a变量的类型是Scanner对象,元素字节数据第57字节一直到第75字节处。接着继续看下面...原创 2020-08-03 18:30:58 · 295 阅读 · 0 评论 -
分析.class字节码文件(上):常量池结构
目录十六进制字节码魔数版本号常量池结构常量池元素父类常量元素变量型常量元素 前面日志说到,Java程序中的类编译后会生成对应的.class字节码文件,里面的数据都是二进制流存储,一个字节码文件由很多部分组成,例如魔数,版本号,常量池,访问权限和接口列表等等,这些数据项对应的字节流都是按照顺序存储的,例如.class文件开头的四个字节一定是魔数,它用来标识该文件是字节码文件,JVM在加载.class文件时都会先检查前面这四个字节,如果发现不是固定的魔数,那么会拒绝加载...原创 2020-07-27 17:45:23 · 426 阅读 · 0 评论 -
数据存储:大端和小端
目录字节序大端和小端大端小端测试写入测试字节码文件的大小端 前面说到,像C/C++这类语言编写的程序,它们被编译后,直接转换成了对应平台上的可被CPU直接运行的机器指令,转换之后,原本语言中的数据结构,例如结构体信息都被替换掉了,换成了各个数据在内存中的地址和偏移量,操作系统并不知道原来的数据的类型,例如一段C程序被编译成汇编语言后,结构体信息被拆成了一条条对内存操作的指令,一个个变量都变成了首地址,原本的C代码结构不复存在。Java则不同,我们知道Java语言因为跨平台性...原创 2020-07-20 17:41:40 · 3160 阅读 · 0 评论 -
.class字节码类型识别:魔数、常量池和内存模型
目录.class文件组成结构魔数常量池oop-klass模型 我们都知道C语言执行依赖编译器,同一段C程序,在不同的操作系统平台(或者说是硬件平台上)上,由不同的编译器将其编译成对应的机器指令,编译后的C程序里各种数据类型信息都被改写成汇编,这是在编译时期完成的,在编译器对C程序进行解析时,识别里面的各种数据类型,例如结构体,并翻译成机器可以理解的二进制程序,待操作系统加载执行。Java则不同,JIT即时编译器虽然也能在编译时识别出Java程序中的各种数据类型信息,但是还记...原创 2020-07-13 17:47:44 · 597 阅读 · 0 评论 -
总结generate_call_stub()现场保存和参数压栈
目录现场保存基地址和变址寄存器参数压栈调用entry_point接收返回值 JVM完成对函数的入参空间计算后,接下来进行栈空间分配,具体的方式上一篇日志总结了,就是入参数量乘上当前操作系统环境指针的宽度,例如32位系统指针宽度为4个字节,64位宽度为8个字节,最后加上调用者函数现场保存过程中寄存器的值,即:入参数量 * 4(或8)字节 + 4(假设有4个寄存器) * 4(或8)字节 也就是说,调用函数总的方法栈空间,除了和入参数量,入参类型有...原创 2020-07-06 17:58:26 · 502 阅读 · 0 评论 -
JVM动态方法栈内存分配
目录运行时动态计算栈大小Java类型实例访问方式参数在方法栈中的存在方式生成机器指令统计栈空间大小 上一篇日志最后写了JVM在函数内部调用其它函数时栈空间的分配方式,也就是调用者函数和被调用者函数两者的方法栈模型,是一种对接的方式,即被调用者分配的方法栈空间,会对接在调用者方法栈栈顶处,这样被调用者函数可以通过ebp寄存器很方便找到调用者函数中压栈的参数,这是方法栈分配方式问题,不过还有一个问题是,JVM怎么知道要为函数方法栈分配多大的空间呢?你可能会想,像C那样,编...原创 2020-06-29 19:42:45 · 636 阅读 · 0 评论 -
generate_call_stub()保存调用者方法栈
目录入参位置物理寄存器Register类保存调用者方法栈栈空间对接 在总结generate_call_stub()保存调用者方法栈过程之前,还是先来回顾下,JVM初始化链是怎么到达该函数的。call_stub()函数里返回一个CallStub类型的函数指针,由_call_stub_entry这一基本类型转换而得到,_call_stub_entry在前面的日志里说到,它最终是unsigned int类型,存放的是某一内存地址,类型转换为CallStub后,也就是cal...原创 2020-06-22 17:45:23 · 430 阅读 · 0 评论 -
_call_stub_entry入口中的pc( )函数
在总结_call_stub_entry之前,先再次回顾下Java主函数调用必须经过的call_stub()函数,展开后得到的结构如下:static CallStub call_stub(){ return (CallStub)(castable_address(_call_stub_entry));} CallStub是自定义类型的函数指针,有八个参数,call_stub()最后返回的就是这么一个函数指针变量类型。castable_address则是一个函数,接收...原创 2020-06-15 17:46:11 · 689 阅读 · 0 评论 -
总结call_stub()中的几个重要参数
接着上一篇日志,先回顾下Java主函数调用时必须执行的call_stub函数:static CallStub call_stub(){ return (CallStub)(castable_address(_call_stub_entry);} castable_address是一个函数,展开后返回的是address_word类型,其最终原型就是unsigned int,_call_stub_entry也是unsigned int类型,指向了某个内存地址。返回值最...原创 2020-06-08 18:09:01 · 779 阅读 · 1 评论 -
CallStub:函数指针直接触发机器指令
上篇日志总结CPU调用函数时的栈内存变化过程,用的C程序解释成汇编来描述,目的是想说明,JVM在执行Java程序时,函数调用的过程和C程序函数调用的过程是相同的,C作为静态编译型的语言,在程序执行先需要编译成CPU能直接执行的二进制码,JVM执行Java程序时也需要先将其解释成字节码,或者说字节码指令集更准确,通常指令集是计算机硬件才有的东西,在开发语言上包装一套指令集,好处是可以统一规范,这样“统一的接口”让开发者用更加接近人类语言来调用机器指令,想想如果让你用汇编来写程序,movl、po...原创 2020-06-01 17:39:21 · 549 阅读 · 0 评论 -
总结汇编函数调用中的栈分配
这篇日志总结一下函数调用过程中,每一个函数的栈空间分配,参数传递等问题,在一些高级语言如Java,C#等,在源程序编译后,交由虚拟机进行解释运行,运行过程中实时翻译成对应操作系统下的机器指令,这样CPU才能直接执行,C语言也如此,不过它是直接编译后运行,而不是执行过程中动态解释运行。无论是像C这种面向过程的,还是Java,C#等面向对象的编程语言,它们都一定要有函数,或者说方法,用来将一个大程序模块化分成一个一个小的功能模块。拿Java举例,JVM的方法调用,并不是CPU直接执行,Java方法...原创 2020-05-25 17:46:44 · 1325 阅读 · 0 评论