Java代码是怎么运行的

  Java代码执行步骤

  编译

  Java文件通过JVM的编译器编译成字节码文件,有了字节码,JVM的类加载器就开始加载字节码文件。

  解释器

  解释器会将字节码转换成汇编指令,然后在转换成CPU可以识别的机器指令(下图是汇编指令转成机器码的案例)。解释器是软件实现的,他将字节码转换成汇编指令,可以实现同一份Java字节码在不同的硬件上运行,而将汇编指令转换成机器指令由硬件直接实现,所以他的速度会更快。JVM为了提升运行效率,会将某些热点代码,一次全部编译成机器指令再执行。也就是和解释执行对应的即时编译,即时编译的机器码存放在一个叫codecache的缓存的地方,这块内存属于堆外内存,如果这块内存不够了,那么即时编译器将不会再进行编译,可能造成程序运行变慢,这也是排查性能问题变慢的一个点。

  

  内存与CPU的配合

  代码转换成了指令以后,指令必须要有上下文环境,这些环境包括:指令寄存器、数据寄存器、栈空间等内存资源(执行机制如下图)。程序被加载进内存以后,指令就在内存中了,指令的指针寄存器IP,指向内存中的下一条待执行指令的地址,CPU的控制单元根据IP寄存器的指向将主存中的指令装载到指令寄存器,这个指令寄存器也就是一个存储设备,不过他集成在CPU内部,指令从主存到达CPU后,只是一串010101的二进制串,还需要通过译码器进行解码,解码后根据运算类型,在从主存中获取操作数,并调用运算单元进行计算。

  我们的数据主要是存储在内存上,然而CPU的计算速度比主存的存取速度快很多倍,所以在两者之间会有多级高级缓存。例如当CPU有个指令是取主存上某一个值,CPU会根据这个值在主存上的位置,去判断是否已经在高速缓存中,如果没有就会去主存中取,取完再放在高速缓存中,这个地方会涉及到一个知识点,就是去主存上读取的时候,并不会仅仅读取一个值,而是把一段长度的值都拿出来并缓存,因为他会假设你既然读了某个位置的值,而这个位置相邻的值也会被读取,就像我们用SQL去查询id=800这行记录的时候,虽然它返回了id=800这行记录,实际上它去读这行记录的时候,把这行记录所在的数据页上的所有数据都放内存里面了,可能下次你去查询id=801行的那条记录的时候,直接就命中缓存,就不用去磁盘去查了,所以我们知道一个缓存行可能缓存了多个字段的值,如果某个进程改了其中一个值,就会导致一整个缓存都会失效,那么这个缓存行上的其他值也会重新从内存读取,所以一些对内存要求比较高的应用,就想规避掉这种情况,比如它们会用对象填充的方式,让某个字段的值可以独占一整个缓存行。

  指令执行

  CPU一通上电就会不停的读取指令、运算,周而复始。CPU会给系统运行中的每一个进程都分配一个时间片,在这个时间片内执行对应的进程的指令,过了这个时间片就执行其他的进程,一个进程内的指令执行的顺序,靠每个指令执行完,再去指向下一个指令的位置。当然一个进程内的某些操作,也会主动放弃CPU的执行权限,比如等待IO的操作(如下图),所以为了让一个进程内的指令可以更高效的执行,我们可以让某个线程在等待IO的时候,其他线程能够获取到CPU的执行权限,并继续执行,如果你的任务都是计算型的任务,基本不会有主动释放CPU的情况,那么在单核机器上就没必要开多线程,如果有大量的IO操作那么多线程的效果就会比较好。

  内存的分配

  一个JVM启动就会产生一个进程,虽然多个进程会共享一个物理内存,但是每个进程都会拥有自己独立的内存空间,当我们同时启动多个JVM并执行如下代码(下图),将会打印这个对象的hashCode,hashCode默认为内存的地址,最后我们发现它打印的都是[email protected],也就是说多个JVM进程返回的内存地址都是一样的,这说明每个进程都有单独的地址空间。

  

  实际上每个进程自己都会维护一个虚拟的内存(参考下图),虚拟存储让每个进程以为自己独占整个内存空间,这样的好处是每个进程都拥有一直的虚拟地址空间,简化了内存管理,进程不需要和其他进程竞争内存空间,因为它是独占的,这也保护了各自进程不会被其他进程破坏。每个进程在申请内存的时候,会维护虚拟内存和物理内存的映射关系,避免其他进程占用自己的内存。

  

 而这个虚拟内存空间可能会超过物理内存,当超过物理内存的时候,可能会发生数据溢出,从而存储到磁盘上。页表保存了虚拟地址和物理地址的映射(如下图),页表是一个数组,每一个元素为一个页的映射关系,这个映射关系可能是和主存的,也可能是和磁盘的,页表存储在主存中,也可能存储在缓冲区,我们将存储在高速缓冲区中的页称为TLAB。

Java反射获取对象属性

  参考下图,它首先要获取这个属性相对对象初始位置的偏移量,如果你持有这个对象的引用,你就能获取到这个对象在虚拟内存中的起始地址,然后我们根据属性的偏移量,就可以获取这个属性的虚拟的内存地址,之后再查询页表就可以获取物理的内存的起始地址,接着再根据这个属性的类型取对应长度的数据。写入也是一样的道理,属性相对对象初始位置的偏移量,在加载这个class的时候就确认好了,它是和class绑定的,那么如果一个对象就一个属性,如果不压缩的话那么除了对象头占128位,这个属性的偏移量可能就是128,如果有多个属性JVM会对属性进行重排序和内存对齐,保证对象占用的大小是8的倍数,另一个作用就是保证一个属性的值,都在一个CPU缓存行中,不然一个属性值会一部分在缓存行A中,一部分在缓存行B中。

关键点:

  1.解释执行和即时编译的区别

  2.CPU是如何访问内存里的数据

  3.JVM里面是如何获取某个对象的属性值

原文地址:https://www.cnblogs.com/aaronzheng/p/12327555.html

时间: 2024-10-16 21:34:49

Java代码是怎么运行的的相关文章

jvm学习-java代码是怎么运行的

首先一个问题入题:是否知道java和c++在运行方式上的区别? java需要依赖JRE的运行环境,而c++代码则无需额外的运行时. 那么问题又来了,既然c++的运行方式已经很成熟了,那么为什么java要在虚拟机中运行呢? 为了实现一次编写,到处运行的目标. jvm作为一个很好的托管环境,能够代替我们处理一些代码中冗长而且容易出错的部分,比如垃圾回收. java虚拟机具体是怎样运行java字节码的? 从虚拟机的视角来看,执行java代码首先需要将它编译而成的class文件加载到java虚拟机中.加

01 Java 代码是怎么运行的

Java代码运行的方式 1:在开发工具中运行 2:双击 jar 文件运行 3:在命令行中运行 4:在网页中运行 上述运行方式都离不开 JRE,也就是 Java 运行时环境.实际上 JRE 仅包含运行 Java 程序的必须组件,包括 Java 虚拟机以及 Java 核心类库等.Java 程序员经常接触到的 JDK 同样包含了 JRE,并且还附带了一系列开发和诊断工具. 为什么 Java 要在虚拟机里运行 Java 是一门高级程序语言,语法复杂,抽象程度高,因此直接在硬件上运行并不现实.所以,在 J

java和javac编译运行java程序

目录 目录 前言 编译命令介绍 javac java 代码编译与运行 java类发现规则 分离class和java文件 参考链接 前言 给自己定了一个月学习JVM虚拟机的计划,不管有没有卵用,学点新东西总是好的. 但是在学习过程中,我想在命令行测试一些JVM参数的时候,发现自己脱离了IDE,竟然无法直接编译java程序!! 我的原则不能抱怨太多,像个Loser,不会的就花时间补回来. 下面,主要是总结一些如何在命令行中编译java程序.主要是想记录总结一下java的类发现原则. 编译命令介绍 其

spark的做算子统计的Java代码(在Linux系统集群式运行)

这篇跟上面一篇java代码部分基本相同,直接上代码 package com.spark.study.core; import java.util.Arrays;import java.util.Iterator; import org.apache.spark.SparkConf;import org.apache.spark.api.java.JavaPairRDD;import org.apache.spark.api.java.JavaRDD;import org.apache.spark

Java代码运行机制

Java代码编译后生成一种与平台无关的字节码(也就是class文件).当然,这种字节码不是可执行的,必须使用Java解释器来解释执行.负责解释执行字节码文件的是Java虚拟机,即JVM.JVM是可运行Java字节码文件的虚拟计算机.所有平台上的JVM向编译器提供相同的编程接口,而编译器只需要面向虚拟机,生成虚拟机理解的代码,然后由虚拟机来解释执行.在一些虚拟机的实现中,还会将虚拟机代码转换成特定系统的机器码执行,从而提高执行效率. 当我们调用Java命令运行某个java程序时,该命令将会启动一条

java代码分析及分析工具

java代码分析及分析工具 一个项目从搭建开始,开发的初期往往思路比较清晰,代码也比较清晰.随着时间的推移,业务越来越复杂.代码也就面临着耦合,冗余,甚至杂乱,到最后谁都不敢碰. 作为一个互联网电子商务网站的业务支撑系统,业务复杂不言而喻.从09年开始一直沿用到现在,中间代码经过了多少人的手,留下了多少的坑,已经记不清楚了,谁也说不清了. 代码的维护成本越来越高.代码已经急需做调整和改善.最近项目组专门设立了一个小组,利用业余时间做代码分析的工作,目标对核心代码进行分析并进行设计重构. 代码分析

Tomcat使用MyEclipse远程调试Java代码配置详解

Tomcat使用MyEclipse远程调试Java代码总结如下:在做远程调试时,在windows系统和非windows系统下的配置,Tomcat中会有所差别,具体如下: 第一步.配置tomcat一.在windows系统中:打开%CATALINE_HOME%/bin下的文件catalina.bat,加入下面这行:set CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket

hibernate配置文件、Java代码与数据库之间的转换

此贴适合初学者,技术大脑请绕道! 一.配置文件 第一步:建立项目工程,以hibernate01为例 第二步:点击项目名称-->Myeclipse--> Project Facets[Capabilities]-->Install Hibernate Facet-->yes 出现下图,选择Target runtime 选择next 包建立后next  配置文件完成 二.生成数据库表(在一的基础上完成) 创建所需要的类和配置文件 例如:    创建包 Test 点击运行 数据表建立成功

Kettle变量和自定义java代码的实例应用

1  kettle.properties参数配置数据源连接和FTP连接 由于测试环境和生产环境中数据库连接FTP等配置会在部署过程中变更,所以预先定义成配置项,在配置文件中修改,这样测试和发布将会变得简单,下面以数据库为例说明这类配置的使用. (1)      首先要找到配置文件,不同的操作系统路径也不一样,本人用win7进行开发,配置文件的路径为"C:\Users\chenpeng\.kettle\kettle.properties",如下: (2)      配置文件中的具体配置如