正文
java检测热点代码 java热搜实现
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
Java 虚拟机一样的速度甚至出现AOT编译方式吗
不论是物理机还是虚拟机,大部分的程序代码从开始编译到最终转化成物理机的目标代码或虚拟机能执行的指令集之前,都会按照如下图所示的各个步骤进行:
其中绿色的模块可以选择性实现。很容易看出,上图中间的那条分支是解释执行的过程(即一条字节码一条字节码地解释执行,如JavaScript),而下面的那条分支就是传统编译原理中从源代码到目标机器代码的生成过程。
如今,基于物理机、虚拟机等的语言,大多都遵循这种基于现代经典编译原理的思路,在执行前先对程序源码进行词法解析和语法解析处理,把源码转化为抽象语法树。对于一门具体语言的实现来说,词法和语法分析乃至后面的优化器和目标代码生成器都可以选择独立于执行引擎,形成一个完整意义的编译器去实现,这类代表是C/C++语言。也可以把抽象语法树或指令流之前的步骤实现一个半独立的编译器,这类代表是Java语言。又或者可以把这些步骤和执行引擎全部集中在一起实现,如大多数的JavaScript执行器。
Javac编译
在Java中提到“编译”,自然很容易想到Javac编译器将*.java文件编译成为*.class文件的过程,这里的Javac编译器称为前端编译器,其他的前端编译器还有诸如Eclipse JDT中的增量式编译器ECJ等。相对应的还有后端编译器,它在程序运行期间将字节码转变成机器码(现在的Java程序在运行时基本都是解释执行加编译执行),如HotSpot虚拟机自带的JIT(Just In Time Compiler)编译器(分Client端和Server端)。另外,有时候还有可能会碰到静态提前编译器(AOT,Ahead Of Time Compiler)直接把*.java文件编译成本地机器代码,如GCJ、Excelsior JET等,这类编译器我们应该比较少遇到。
下面简要说下Javac编译(前端编译)的过程。
词法、语法分析
词法分析是将源代码的字符流转变为标记(Token)集合。单个字符是程序编写过程中的的最小元素,而标记则是编译过程的最小元素,关键字、变量名、字面量、运算符等都可以成为标记,比如整型标志int由三个字符构成,但是它只是一个标记,不可拆分。
语法分析是根据Token序列来构造抽象语法树的过程。抽象语法树是一种用来描述程序代码语法结构的树形表示方式,语法树的每一个节点都代表着程序代码中的一个语法结构,如bao、类型、修饰符、运算符等。经过这个步骤后,编译器就基本不会再对源码文件进行操作了,后续的操作都建立在抽象语法树之上。
填充符号表
完成了语法分析和词法分析之后,下一步就是填充符号表的过程。符号表是由一组符号地址和符号信息构成的表格。符号表中所登记的信息在编译的不同阶段都要用到,在语义分析(后面的步骤)中,符号表所登记的内容将用于语义检查和产生中间代码,在目标代码生成阶段,党对符号名进行地址分配时,符号表是地址分配的依据。
语义分析
语法树能表示一个结构正确的源程序的抽象,但无法保证源程序是符合逻辑的。而语义分析的主要任务是读结构上正确的源程序进行上下文有关性质的审查。语义分析过程分为标注检查和数据及控制流分析两个步骤:
标注检查步骤检查的内容包括诸如变量使用前是否已被声明、变量和赋值之间的数据类型是否匹配等。
数据及控制流分析是对程序上下文逻辑更进一步的验证,它可以检查出诸如程序局部变量在使用前是否有赋值、方法的每条路径是否都有返回值、是否所有的受查异常都被正确处理了等问题。
字节码生成
字节码生成是Javac编译过程的最后一个阶段。字节码生成阶段不仅仅是把前面各个步骤所生成的信息转化成字节码写到磁盘中,编译器还进行了少量的代码添加和转换工作。 实例构造器init()方法和类构造器clinit()方法就是在这个阶段添加到语法树之中的(这里的实例构造器并不是指默认的构造函数,而是指我们自己重载的构造函数,如果用户代码中没有提供任何构造函数,那编译器会自动添加一个没有参数、访问权限与当前类一致的默认构造函数,这个工作在填充符号表阶段就已经完成了)。
JIT编译
Java程序最初是仅仅通过解释器解释执行的,即对字节码逐条解释执行,这种方式的执行速度相对会比较慢,尤其当某个方法或代码块运行的特别频繁时,这种方式的执行效率就显得很低。于是后来在虚拟机中引入了JIT编译器(即时编译器),当虚拟机发现某个方法或代码块运行特别频繁时,就会把这些代码认定为“Hot Spot Code”(热点代码),为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,完成这项任务的正是JIT编译器。
现在主流的商用虚拟机(如Sun HotSpot、IBM J9)中几乎都同时包含解释器和编译器(三大商用虚拟机之一的JRockit是个例外,它内部没有解释器,因此会有启动相应时间长之类的缺点,但它主要是面向服务端的应用,这类应用一般不会重点关注启动时间)。二者各有优势:当程序需要迅速启动和执行时,解释器可以首先发挥作用,省去编译的时间,立即执行;当程序运行后,随着时间的推移,编译器逐渐会返回作用,把越来越多的代码编译成本地代码后,可以获取更高的执行效率。解释执行可以节约内存,而编译执行可以提升效率。
HotSpot虚拟机中内置了两个JIT编译器:Client Complier和Server Complier,分别用在客户端和服务端,目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式工作。
运行过程中会被即时编译器编译的“热点代码”有两类:
被多次调用的方法。
被多次调用的循环体。
两种情况,编译器都是以整个方法作为编译对象,这种编译也是虚拟机中标准的编译方式。要知道一段代码或方法是不是热点代码,是不是需要触发即时编译,需要进行Hot Spot Detection(热点探测)。目前主要的热点 判定方式有以下两种:
基于采样的热点探测:采用这种方法的虚拟机会周期性地检查各个线程的栈顶,如果发现某些方法经常出现在栈顶,那这段方法代码就是“热点代码”。这种探测方法的好处是实现简单高效,还可以很容易地获取方法调用关系,缺点是很难精确地确认一个方法的热度,容易因为受到线程阻塞或别的外界因素的影响而扰乱热点探测。
基于计数器的热点探测:采用这种方法的虚拟机会为每个方法,甚至是代码块建立计数器,统计方法的执行次数,如果执行次数超过一定的阀值,就认为它是“热点方法”。这种统计方法实现复杂一些,需要为每个方法建立并维护计数器,而且不能直接获取到方法的调用关系,但是它的统计结果相对更加精确严谨。
在HotSpot虚拟机中使用的是第二种——基于计数器的热点探测方法,因此它为每个方法准备了两个计数器:方法调用计数器和回边计数器。
方法调用计数器用来统计方法调用的次数,在默认设置下,方法调用计数器统计的并不是方法被调用的绝对次数,而是一个相对的执行频率,即一段时间内方法被调用的次数。
回边计数器用于统计一个方法中循环体代码执行的次数(准确地说,应该是回边的次数,因为并非所有的循环都是回边),在字节码中遇到控制流向后跳转的指令就称为“回边”。
在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阀值,当计数器的值超过了阀值,就会触发JIT编译。触发了JIT编译后,在默认设置下,执行引擎并不会同步等待编译请求完成,而是继续进入解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成为止(编译工作在后台线程中进行)。当编译工作完成后,下一次调用该方法或代码时,就会使用已编译的版本。
由于方法计数器触发即时编译的过程与回边计数器触发即时编译的过程类似,因此这里仅给出方法调用计数器触发即时编译的流程:
java如何同时检测局域网中的多个ip是否成功连接?
多线程执行下列代码java检测热点代码:
InetAddress addr = InetAddress.getByName(ip);
if (addr.isReachable(5000)) {
//这就是连上java检测热点代码了
} else {
//这就是没连上
}
Java编程怎样编辑自动打开热点
一般java使用的都是MyEclipse或者eclipse工具编程,
一般情况下也可以使用别的工具,记事本是最简单的了!
第一步:新建一个文本文档写java代码,写完后,另存为 【XXX.java】后缀名为java类型的文件,记得是后缀名,而不是后面有.java就行了!
第二步:然后Win+R--cmd-- 回车。打开cmd命令窗口。然后加入你那个java文件的文件夹路径,(右击java文件复制属性就有了,然后在cmd命令窗口中输入: cd +路径 记得cd与路径间有空格的)要是你的文件时房在C盘的,输入【C:】回车就OK了,D盘输入【D:】,以此类推,然后输入:javac XXX.java (回车)编译成功就会产生一个class文件在当前目录。然后再输入:java XXX (回车)结果就出来了
还有就是,一般java编程是需要编程环境的,需要安装JDK,谢谢
编译告警被关闭的参数
javac参数 编译警告关闭_JVM之JIT即时编译 原创
2020-12-26 08:21:34

水天姬 
码龄4年
关注

当今Java语言被广为接受的优点之一就有即时编译,即时编译的存在使得Java应用可以运行时间的增长而获得更高的性能。
如果有对jvm做过研究的朋友,一定听说过这样一段话:由于即时编译技术的进步,尤其是逃逸分析技术的日渐强大,栈上分配、标量替换优化手段已经导致一些微妙的变化悄然发生,Java对象实例都分配在堆上也变得不那么绝对了。虚拟机发展到今天,即时编译也是我们对jvm讨论的话题之一,今天我也来对即时编译进行一个详述。
JIT(just-in-time)
Coding的哔哔叨叨
JIT即just-in-time的缩写,即时编译技术,可以加快java的执行速度。我们都知道,从源代码,经javac编译生成.class文件字节码,在经过类加载器,加载到内存,jvm通过解释字节码翻译成对应的机器指令,逐条读入,逐条翻译解释,这就是 jvm解释执行的过程。
很显然,解释执行,其执行速度必然会比可执行的二进制字节码程序慢很多,为了提高执行速度,引入了即时编译技术。
Java程序最初是通过解释器(interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁,就会把这些代码认为是“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机会把这些代码编译成与本地机器相关的机器码,并进行各种层次的优化,完成这个任务的编译器称之为“即时编译器(just-in-time compiler)”。
注意:即时编译器并不是java虚拟机的必须部分,java虚拟机规范并没有规定必须要有即时编译器的存在,并且,更没有要求或指导如何实现即时编译器;但是即时编译器的好坏、代码优化程度高低却是衡量一款商用虚拟机的重要指标之一,它也是虚拟机最核心且最能体现虚拟机技术水平的的部分。
由于上述原因,所以即时编译器完全是某虚拟机具体实现的相关内容,所以,如无特殊说明,本文所提到的即时编译器都是Hotspot虚拟机内的即时编译器;所提到的虚拟机也是专门特指Hotspot虚拟机。
JVM运行原理
Coding的哔哔叨叨
这儿我们贴出一张图,大家结合着图进行理解即可。


为什么Hotspot要使用解释器与编译器并存的架构?
Coding的哔哔叨叨
首先要说明,并不是所有的虚拟机实现都采用解释器与编译器并行的方案,但许多的主流虚拟机,如Hotspot,都同时包含解释器和编译器。解释器与编译器各有优势: 当程序需要快速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行;当程序运行环境内存资源限制较大(如嵌入式系统),可以使用解释器节约内存。反之可以使用编译器提高执行效率,此外,编译后如果出现“罕见陷阱”,可以逆优化退回到解释执行。

编译的时间开销
Coding的哔哔叨叨
我们先抽象看下解释器和编译器的执行过程。
解释器:输入代码-[解释器解释执行]-执行结果
编译器:输入代码-[编译器编译]-编译后的代码-执行结果。
从上面的抽象执行过程来看,JIT编译执行,要比解释执行慢,多了一个执行编译后代码的过程,所以针对“只执行一次的代码”,解释执行总要比编译执行快。而我们通常所说的,JIT编译执行,比解释执行快,并不是说“编译”这个动作比“解释”这个动作快,而是在非“只执行一次的代码”环境下,JIT编译执行要比解释执行快,要注意这个前提环境,而我们所指的不止执行一次的代码,有以下两个场景:
方法被多次调用,相反类似类构造器(class initializer,())只被调用一次。
存在循环。
只有频繁执行的代码,才会保证JIT编译执行的正向收益。
编译的空间开销
Coding的哔哔叨叨
对java语言来说,编译后的代码相对于class字节码,膨胀达10倍是很正常的,同上面所说的时间开销一样,只有针对频繁执行的代码,才能保证有正向收益,才值得去编译,如果把所有的代码都进行编译,会很明显的增加代码所占空间。
JIT编译,会将编译后的汇编指令保存在代码缓存中,代码缓存是固定大小的,当jvm代码缓存一旦被填满,jvm就不能编译更多的代码。
所以,这也解释了,为什么虚拟机不完全采用JIT编译执行,而是选择解释器与编译器混合共存的执行引擎。
现在并没有一个好的机制可以确定一个特定的应用到底需要多大的代码缓存。因此,当需要提高代码缓存时,这将是一种凑巧的操作,一个通常的做法是将代码缓存变成默认大小的两倍或四倍。
可以通过 –XX:ReservedCodeCacheSize=Nflag(N 就是之前提到的默认大小)来最大化代码缓存大小。代码缓存的管理类似于 JVM 中的内存管理:有一个初始大小(用-XX:InitialCodeCacheSize=N 来声明)。代码缓存的大小从初始大小开始,随着缓存被填满而逐渐扩大。代码缓存的初始大小是基于芯片架构(例如 Intel 系列机器,client 编译器模式下代码缓存大小起始于 160KB,server 编译器模式下代码缓存大小则起始于 2496KB)以及使用的编译器的。重定义代码缓存的大小并不会真正影响性能,所以设置 ReservedCodeCacheSize 的大小一般是必要的。
再者,如果 JVM 是 32 位的,那么运行过程大小不能超过 4GB。这包括了 Java 堆,JVM 自身所有的代码空间(包括其本身的库和线程栈),应用程序分配的任何的本地内存,当然还有代码缓存。
所以说代码缓存并不是无限的,很多时候需要为大型应用程序来调优(或者甚至是使用分层编译的中型应用程序)。比如 64 位机器,为代码缓存设置一个很大的值并不会对应用程序本身造成影响,应用程序并不会内存溢出,这些额外的内存预定一般都是被操作系统所接受的。
Hotspot的两个不同编译器
Coding的哔哔叨叨
Hotspot内置了两个解释编译器: client compiler(c1)和server compiler(c2),分别用在客户端和服务端。HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用“-client”或“-server”参数去强制指定虚拟机运行在Client模式或Server模式。c1编译器有更快的编译速度,c2编译器有更高的编译质量。
哪些代码会被即时编译?
Coding的哔哔叨叨
哪些代码会被称为热点代码,其实我们在上面也提过过,以下两种场景:
被多次调用的方法。
被多次执行的循环体。
注意:上面两种情况,编译器都是以整个方法作为编译对象,编译方法发生在方法执行过程中,我们称之为:栈上替换(OSR,On Stack Replacement),方法栈针还在栈上,方法就被替换了。
如何编译为本地代码?
Coding的哔哔叨叨
Server Compiler和Client Compiler两个编译器的编译过程是不一样的。 对Client Compiler来说,它是一个简单快速的编译器,主要关注点在于局部优化,而放弃许多耗时较长的全局优化手段。 而Server Compiler则是专门面向服务器端的,并为服务端的性能配置特别调整过的编译器,是一个充分优化过的高级编译器。
HotSpot的热点探测技术
Coding的哔哔叨叨
目前主流的热点探测技术有两种:
基于采样的热点探测
从字面也能基本理解,这种方法基本是周期性的检查各个线程的栈顶,如果发现某个方法经常性的出现在栈顶,那么这个方法就是“热点代码”。这种方式简单有效,但是也存在很明显的问题,很难精准的确认一个方法是热点方法,容易因为线程阻塞或者其他的一些外界因素的影响而扰乱热点探测。
基于计数器的热点探测
采用这种方式的虚拟机会为每个方法(甚至是代码)建立计数器,统计方法的执行次数,如果执行次数超过一定的阈值,就认为是“热点方法”。这种方式实现起来就复杂了,需要为每个方法建立并维护计数器,而且不能直接获取方法的调用关系,但是这种方式的统计结果就相对精确严谨。 Hotspot虚拟机采用的是计数器热点探测方式。 ,为每个方法都维护了两个计数器:方法调用计数器和回边计数器。在确定虚拟机参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。
方法调用计数器
顾名思义,这个计数器用于统计方法被调用的次数。当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将此方法的调用计数器值加1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值。如果超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。如果不做任何设置,执行引擎并不会同步等待编译请求完成,而是继续进行解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成。当编译工作完成之后,这个方法的调用入口地址就会系统自动改写成新的,下一次调用该方法时就会使用已编译的版本。

回边计数器
它的作用就是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”。
JIT的知识点,大部分应该都提到了,还有啥没提到的,留言,补充。
文章部分图片来源于网络其他优秀工程师的分享。
最后还想给大家补充个知识点,关于java的javac编译和即时编译。在刚开始接触到java的时候,每次谈到编译,总是搞不清到底说的是那种编译,在这也简单聊一下,给大家做个简单的区分。
javac编译与JIT编译
Coding的哔哔叨叨
我们通常所说的编译,有两种情形,一种是从源代码(.java)编译为字节码(.class);一种是虚拟机执行字节码的过程,从字节码编译为本地机器语言。所以在提到编译的时候,一定要区分清是哪种(刚开始结束java的时候被这个搞得晕乎的很久)。我们一般把javac的编译过程称之为:前端编译;把JIT编译称之为后端编译。如果可能,下篇文章,我把java的前端编译过程进行一下说明。
不积跬步,无以至千里。
文章有帮助的话,点个转发、在看呗。
谢谢支持哟 (*^__^*)
END
?

相关资源:java编译器源码解析-warnings-ng-plugin:Jenkins警告插件-下一代
java如何检测源码二次倒卖
1、可以使用源代码监测软件进行检测,它能够快速扫描整个源码,并检测其中的可疑字符串,如果发现有任何可疑的字符串,就会发出警告。
2、源代码文件也可以使用特征哈希技术进行比对,它可以检测出源代码文件是否与其他文件相同,如果发现有不一致的情况,就可以判断是二次倒卖。
3、可以使用版权保护工具,它可以防止源代码被倒卖或者盗版,同时也可以追踪被倒卖的源码,以便及时发现二次倒卖的行为。
java检测热点代码的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于java热搜实现、java检测热点代码的信息别忘了在本站进行查找喔。