JVM性能魔术技巧

HotSpot是我们众所周知和喜爱的JVM,是Java和Scala汁流淌的大脑。 多年来,许多工程师对其进行了改进和调整,并且在每次迭代中,其代码执行的速度和效率都接近本机编译代码。 duke01-e1369743413155

JIT(“即时”)编译器是其核心。 该组件的唯一目的是使您的代码快速运行,这是HotSpot如此受欢迎和成功的原因之一。

JIT编译器实际上是做什么的?

在执行代码时,JVM会收集有关其行为的信息。 一旦收集了有关热方法的足够统计信息(默认阈值为10K调用),编译器就会启动,并将该方法的与平台无关的“慢”字节码转换为自身的优化,精简,平均编译版本。

一些优化是显而易见的:简单的方法内联,清除无效代码,用本机数学运算替换库调用等。请注意,JIT编译器不会就此停止。 这是它执行的一些更有趣的优化:

分而治之

您使用以下模式多少次:

StringBuilder sb = new StringBuilder("Ingredients: ");for (int i = 0; i < ingredients.length; i++) {if (i > 0) {sb.append(", ");}sb.append(ingredients[i]);
}return sb.toString();

也许这个:

boolean nemoFound = false;for (int i = 0; i < fish.length; i++) {String curFish = fish[i];if (!nemoFound) {if (curFish.equals("Nemo")) {System.out.println("Nemo! There you are!");nemoFound = true;continue;}}if (nemoFound) {System.out.println("We already found Nemo!");} else {System.out.println("We still haven't found Nemo : (");}
}

这两个循环的共同点是,在这两种情况下,循环都会做一件事一段时间,然后从某个角度开始做另一件事。 编译器可以发现这些模式,并将循环分成多个案例,或“剥离”几次迭代。

让我们以第一个循环为例。 if (i > 0)行在一次迭代中从false开始,并且从那一点开始始终计算为true 。 为何每次都要检查状况? 编译器将编译该代码,就像这样编写:

StringBuilder sb = new StringBuilder("Ingredients: ");if (ingredients.length > 0) {sb.append(ingredients[0]);for (int i = 1; i < ingredients.length; i++) {sb.append(", ");sb.append(ingredients[i]);}
}return sb.toString();

这样,即使某些代码可能在进程中重复,冗余的if (i > 0)也将被删除,因为速度就是它的全部。

生活在边缘

空检查是一丁点的。 有时null对于我们的引用是有效值(例如,指示缺少值或错误),但有时为了安全起见,我们添加了null检查。

其中一些检查可能永远不会失败(就此而言,null表示失败)。 一个经典的示例将包含一个断言,如下所示:

public static String l33tify(String phrase) {if (phrase == null) {throw new IllegalArgumentException("phrase must not be null");}return phrase.replace('e', '3');
}

如果您的代码运行良好,并且从未将null作为l33tify的参数l33tify ,则断言将永远不会失败。

在多次执行此代码而没有进入if语句的主体之后,JIT编译器可能会乐观地认为此检查很有可能是不必要的。 然后它将继续编译该方法,将检查全部丢弃,就好像是这样写的:

public static String l33tify(String phrase) {return phrase.replace('e', '3');
}

这可以显着提高性能,这在大多数情况下可能是纯粹的胜利。

但是,如果那个幸福道路的假设最终被证明是错误的呢?

由于JVM现在正在执行本机已编译的代码,因此null引用不会导致模糊的NullPointerException ,而是导致实际的,苛刻的内存访问冲突。 JVM是它的低级生物,它将拦截产生的分段错误,进行恢复,并进行反优化处理-编译器不能再假设null检查是多余的:它重新编译该方法,这次使用null检查。

虚拟精神错乱

JVM的JIT编译器与其他静态编译器(如C ++编译器)之间的主要区别之一是,JIT编译器具有动态运行时数据,决策时可以依靠该数据来运行。

方法内联是一种常见的优化方法,在该方法中,编译器采用一个完整的方法并将其代码插入另一个程序中,以避免调用方法。 在处理虚拟方法调用(或动态调度 )时,这会有些棘手。

以以下代码为例:

public class Main {public static void perform(Song s) {s.sing();}
}public interface Song { void sing(); }public class GangnamStyle implements Song {@Overridepublic void sing() {System.out.println("Oppan gangnam style!");}
}public class Baby implements Song {@Overridepublic void sing() {System.out.println("And I was like baby, baby, baby, oh");}
}// More implementations here

该方法perform可能被执行数百万次,每一次方法的调用sing发生。 调用是昂贵的,尤其是诸如此类的调用,因为调用需要根据s的运行时类型每次动态选择要执行的实际代码。 在这一点上,内联似乎是一个遥不可及的梦想,不是吗?

不必要! 执行后, perform几千次,编译器可能会决定,根据其收集的统计数据,该调用的95%的目标的一个实例GangnamStyle 。 在这些情况下,HotSpot JIT可以执行乐观优化,以消除虚拟的sing调用。 换句话说,编译器将为这些代码生成本机代码:

public static void perform(Song s) {if (s fastnativeinstanceof GangnamStyle) {System.out.println("Oppan gangnam style!");} else {s.sing();}
}

由于此优化依赖于运行时信息,因此即使它是多态的,它也可以消除大多数sing调用。

JIT编译器还有很多技巧,但是这些只是一些技巧,可让您了解当我们的代码由JVM执行和优化时的幕后故事。

我是否能帮助?

JIT编译器是面向简单人员的编译器; 它旨在优化简单的编写,并搜索出现在日常标准代码中的模式。 帮助您的编译器的最好方法是不要太努力地帮助它-只需编写代码即可。

参考:来自Takipi博客的JCG合作伙伴 Niv Steingarten的JVM性能魔术技巧 。

翻译自: https://www.javacodegeeks.com/2013/06/jvm-performance-magic-tricks.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/368169.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动端1px

移动端不同尺寸设备dpi不同&#xff0c;会造成1px线条不同程度的缩放&#xff0c;可利用媒体查询device-pixel-ratio&#xff0c;进行不同情况匹配&#xff1a; media&#xff08;-webkit-min-device-pixel-ratio:1.5&#xff09;,(min-device-pixel-ratio:1.5){//dpi:1.5 .bo…

mysql 10个日期,MySQL自学篇(10)——日期函数

MySQL自学篇(十)——日期函数日期和时间函数(1)获取当前日期的函数和时间的函数CURDATE()和CURRENT_DATE()函数&#xff0c;获取当前日期select current_date(),curdate(),curdate()0;curdate()0 表示将当前时间转化为数值型CURTIME()和CURRENT_TIME()获取当前时间select cur…

python-flask-请求源码流程

启动先执行manage.py 中的 app.run() class Flask(_PackageBoundObject):   def run(self, hostNone, portNone, debugNone, **options):from werkzeug.serving import run_simpletry:#run_simple 是werkzeug 提供的方法&#xff0c;会执行第三个参数 self()run_simple(ho…

正则表达式强化,爬虫练习

re模块下的常用方法 import re ret re.findall(\d(\.\d)?, 1.232.34)   print(ret) 结果&#xff1a; [.23,.34] # findall的正则表达式里面有分组(),()里面的内容优先显示 ret re.findall(\d(?:\.\d)?, 1.232.34)print(ret)    结果&#xff1a;…

Java垃圾回收(3)

这是我之前的两个垃圾收集博客文章中的内容&#xff1a; 热点GC概述 。 并行垃圾收集器 。 并发标记扫描 Hotspot中的并行垃圾收集器旨在最大程度地减少应用程序进行垃圾收集所花费的时间&#xff0c;这称为吞吐量 。 对于所有应用程序来说&#xff0c;这并不是一个适当的权…

display转块状化

display:block block元素会独占一行&#xff0c;多个block元素会各自新起一行。默认情况下&#xff0c;block元素宽度自动填满其父元素宽度。 block元素可以设置width,height属性。块级元素即使设置了宽度,仍然是独占一行。 block元素可以设置margin和padding属性。 display:in…

php论坛思路,PHP论坛实现积分系统的思路代码详解

PHP论坛实现积分系统的思路代码详解,积分,头像,等级,用户,字段PHP论坛实现积分系统的思路代码详解易采站长站&#xff0c;站长之家为您整理了PHP论坛实现积分系统的思路代码详解的相关内容。首先在用户表定义一个积分字段&#xff1b;然后创建一个等级表&#xff0c;主要字段有…

Spring MVC:会话高级

不久前&#xff0c;我写了一篇关于Spring MVC应用程序中HTTP会话的文章。 那是简单的文章&#xff0c;着重于用法的实际方面。 在文章的最后&#xff0c;我保证会写一些更高级的主题&#xff0c;专门针对Spring MVC应用程序中的会话。 因此&#xff0c;我将发布这些东西。 在开…

Linux LVM管理

LVM(Logical Volume Manager)逻辑卷管理是在Linux2.4内核以上实现的磁盘管理技术。它是Linux环境下对磁盘分区进行管理的一种机制。 本文内容&#xff1a; 创建和管理LVM扩容LVM分区一、创建和管理LVM 要创建一个LVM系统&#xff0c;一般需要经过以下步骤&#xff1a; 1、 创建…

USB OTG插入检测识别

一 USB引脚一般四根线&#xff0c;定义如下&#xff1a; 为支持OTG功能&#xff0c;mini/micro usb接口扩展了一个ID引脚&#xff08;第4脚&#xff09; A设备端ID脚接地&#xff0c;则初始状态为Host&#xff0c;例如PC和支持OTG设备做主设备时 B设备端ID脚悬空&#xff0c;默…

CSS3与页面布局学习笔记(三)——BFC、定位、浮动、7种垂直居中方法

一、BFC与IFC 1.1、BFC与IFC概要 BFC&#xff08;Block Formatting Context&#xff09;即“块级格式化上下文”&#xff0c; IFC&#xff08;Inline Formatting Context&#xff09;即行内格式化上下文。常规流&#xff08;也称标准流、普通流&#xff09;是一个文档在被显示…

matlab空间截面回归,截面空间回归模型操作应用手册

原标题&#xff1a;截面空间回归模型操作应用手册SAR模型数据集包含对地理区域或其他单元的观测;所以需要的是有一些距离的度量标准来区分哪些单位彼此之间比较近。spregress命令对横断面数据进行建模。它要求每一个观察都代表一个独特的空间单元。对于每个单元(即面板数据)有多…

Java垃圾回收(2)

并行清理 今天&#xff0c;我们介绍了并行GC的工作原理。 具体来说&#xff0c;这是在Eden上运行Parallel Scavenge收集器&#xff0c;在Tenured一代中运行Parallel Mark and Sweep收集器的组合。 您可以通过传递-XX&#xff1a; UseParallelOldGC来获得此选项&#xff0c;尽管…

Navicat Premium创建MySQL存储过程

1、使用Navicat Premium打开创建函数向导&#xff0c;操作&#xff1a;连接名——数据库——函数——新建函数 2、选择过程——输入存储过程参数——完成&#xff08;这一步可以不填写参数&#xff0c;编写存储过程代码的时候设置参数&#xff09; 3、按照要求完成存储过程代码…

CSS3与页面布局学习笔记(二)——盒子模型(Box Model)、边距折叠、内联与块标签、CSSReset

一、盒子模型&#xff08;Box Model&#xff09; 盒子模型也有人称为框模型&#xff0c;HTML中的多数元素都会在浏览器中生成一个矩形的区域&#xff0c;每个区域包含四个组成部分&#xff0c;从外向内依次是&#xff1a;外边距&#xff08;Margin&#xff09;、边框&#xff…

mysql中将某个字段做计算,mysql创建计算字段使用子查询教程

作为计算字段使用子查询使用子查询的另一方法是创建计算字段。假如需要显示 customers表中每个客户的订单总数。订单与相应的客户ID存储在 orders 表中。为了执行这个操作&#xff0c;遵循下面的步骤。(1) 从 customers 表中检索客户列表。(2) 对于检索出的每个客户&#xff0c…

像Java这样的C ++具有低延迟

总览 以前&#xff0c;我写过一篇有关Java之类的C的文章。 这是我以前遇到的术语。 但是&#xff0c;经过思考&#xff0c;我认为像Java这样的C 是一个更好的术语&#xff0c;因为您仍在使用OOP做法&#xff08;不是C风格的&#xff09;&#xff0c;但是您需要自己进行更多的工…

项目一总结

1、编程逻辑不明确的问题 在项目开始阶段&#xff0c;项目分工应根据项目模块进行详细分工&#xff0c;公用部分应尽量节省时间 2、对于文件命名规则的定义&#xff0c;现在项目名字命名很乱&#xff0c;在项目整合时会出很多问题 3、文件目录不清晰&#xff0c;条理性差 4、代…

Android GreenDao使用教程

一、Greendao简介 Greendao是一款用于数据库创建与管理的框架&#xff0c;由于原生SQLite语言比较复杂繁琐&#xff0c;使得不少程序员不得不去学习SQLite原生语言&#xff0c;但是学习成本高&#xff0c;效率低下&#xff0c;所以不少公司致力于开发一款简单的数据库管理框架&…

matlab如何将相近的数据,matlab新手,求帮助!主要是如何将数据和公式导入

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼这是别人给我的程序&#xff0c;我想问一下怎么把我的两个excel数据导入&#xff0c;以及公式怎么写&#xff1f;clc;clear all;close all;datadlmread(all_data_in_situ_lxz.txt);data(6,:)[];data(12,:)[];data(13,:)[];data(13,…