JIT(即时编译)

解释型代码:程序可移植,相同的代码在任何有适当解释器的机器上,都能运行,但是速度慢。

编译型代码:速度快,电视不同CPU平台的代码无法兼容。

java则是使用java的编译器先将其编译为class文件,也就是字节码;然后将字节码交由jvm(java虚拟机)解释执行。由于这个编译是在程序执行时进行的,因此被称为“即使编译”。

热点编译

对于程序来说,通常只有一部分代码被经常执行,而应用的性能就取决于这些代码执行得有多快。这些关键代码段被称为应用的热点,代码执行得越多就被认为是越热。

因此JVM执行代码时,并不会立即编译代码:

  1. 如果代码只执行一次,那编译完全就是浪费精力。对于只执行一次的代码,解释执行Java字节码比先编译然后执行的速度快。
  2. JVM执行特定方法或者循环的次数越多,它就会越了解这段代码。这使得JVM可以在编译代码时进行大量优化。

分层编译

Client编译器和server编译器主要的区别在于编译代码的时机不同。client编译器开启编译比server编译器要早。这意味着在代码执行的开始阶段,client编译器比server编译器要快,因为它的编译代码相比server编译器而言要多。

分层编译是综合了client和server的优点。在开启分层编译(-XX:+TieredCompilation)后代码先由client编译器编译,随着代码变热,由server编译器重新编译。

调优代码缓存

JVM编译代码时,会在代码缓存中保留编译之后的汇编语言指令集。代码缓存的大小固定,所以一旦填满,JVM就不能编译更多代码了。

也就是说,如果代码缓存过小,那么就会有一些热点代码被编译了,而其他没有,最终导致应用的大部分代码都是解释运行(非常慢)。这个问题在使用client编译器或进行分层编译时很常见。

当代码缓存填满时,JVM通常会发出以下警告:

Java HotSopt(TM) 64-Bit Server VM warning:CodeCache is full.Compiler has bean disabled.
Java HotSopt(TM) 64-Bit Server VM warning:Try increasing the code cache size using -XX:ReservedCodeCacheSize=

各平台代码缓存的默认大小:

jvm jdk版本 大小
32位client Java8 32MB
32位client 分层编译,Java8 240MB
64位client 分层编译,Java8 240MB
32位client Java7 32MB
32位server Java7 32MB
64位server Java7 48MB
64位server 分层编译,Java7 48MB

如果代码缓存设为1GB,JVM就会保留1GB的本地内存空间。如果是32位JVM,那么进程占用的总内存不能超过4GB(包括Java堆、JVM自身所有代码占用空间、分配给应用的本地内存、代码缓存)。

通过jconsole Memory(内存)面板的Memory Pool Code Cache图表,可以监控代码缓存。

编译阈值

一旦代码执行到一定次数,且达到了编译阈值,编译器就可以获得足够的信息编译代码了。

编译是基于两种JVM计数器的:方法调用计数器和方法中的循环回边计数器。回边实际上可以看作是循环完成执行的次数。

栈上替换:JVM可以在方法循环运行时进行编译,并在循环代码编译结束之后,JVM替换还在栈上的代码,循环的下一次迭代就会执行快的多的代码。

标准编译由-XX:CompileThreshold=N标志触发。使用client编译器时,N的默认值是1500,使用server编译器时为10000。

计数器会随着时间而减少,所以计数器只是方法或循环最新热度的度量。由此带来一个副作用是,执行不太频繁的代码可能永远不会编译。

检测编译过程

-XX:+PrintCompilation

如果开启PrintCompilation,每次编译一个方法(或循环)时,JVM就会打印一行被编译的内容信息。

绝大多数编译日志的行具有以下格式:

timestamp compilation_id attributes (tiered_level) method_name size deopt

timestamp表示编译完成的时间

compilation_id内部的任务ID

attributes是一组5个字符长的串,表示代码编译的状态。如果给定的编译被赋予了特定属性,就会打印下面列表中所显示的字符,否则该属性就打印一个空格。

* % :编译为OSR

* s :方法是同步的

* !:方法有异常处理器

* b :阻塞模式时发生的编译

* n:为封装本地方法所发生的编译

tiered_level 如果程序没有使用分成编译的方式运行则为空,否则为数字,表明所完成编译的级别

method_name格式为:ClassName::method

然后是编译后代码大小(单位是字节)

最后,在某些情况下,编译日志的结尾会有一条信息,表明发生了某种逆优化,通常是“made not entrant”或”made zombie”

    135    1     n 0       java.lang.Thread::currentThread (native)   (static)
136 2 3 java.util.Arrays::copyOf (19 bytes)
136 7 3 sun.nio.cs.UTF_8$Encoder::encode (359 bytes)
137 8 2 java.lang.String::hashCode (55 bytes)

使用jstat -compiler 进程ID 也可以看有多少方法被编译

使用jstat -printcompilation 5003 1000 表示进程ID为5003的程序每1秒输出一次最近被编译的方法

编译器线程

当方法(或循环)适合编译时,就会进入到编译队列。队列则由一个或多个后台线程处理。编译队列是一种优先队列,即调用计数次数多的方法有更高的优先级。

当开启分层编译时,JVM默认开启多个client和server线程。

cpu数量 C1的线程数(client) C2的线程数(server)
1 1 1
2 1 1
4 1 2
8 1 2
16 2 6
32 3 7
64 4 8
128 4 10

编译器的线程数可通过-XX:CICompilerCount=N标志来设置。对于分层编译来说,设置的值中三分之一将用来处理client编译器队列,其余的线程(至少一个)用来处理server编译器队列。

使用分层编译时,线程数很容易超过系统限制,特别是有多个JVM同时运行的时候。在这种情况下,减少线程数有助于提高整体的吞吐量(尽管代价可能是热身期会持续得更长)。

方法内联

public class Point{
private int x,y;
public int getX(){ return x; }
public void setX(int i){ x = i;}
}

如果你写下面的代码

Point p = getPoint();
p.setX(p.getX()*2);

编译后的代码本质上执行的是:

Point p = getPoint();
p.x = p.x *2;

方法是否内联取决于它有多热以及它的大小。

-XX:MaxInlineSize=N默认是35字节,即只有方法小于35字节时第一次调用方法时就会被内联。

-XX:MaxFreqInlineSize=N默认是325字节,即只有当一个方法频繁被调用并且小于325字节时会被内联。

逃逸分析

-XX:+DoEscapeAnalysis默认为true。逃逸分析可以让JVM对一个对象根据代码来进行优化。

  1. 栈上分配

    我们都知道Java中的对象都是在堆上分配的,而垃圾回收机制会回收堆中不再使用的对象,但是筛选可回收对象,回收对象还有整理内存都需要消耗时间。如果能够通过逃逸分析确定某些对象不会逃出方法之外,那就可以让这个对象在栈上分配内存,这样该对象所占用的内存空间就可以随栈帧出栈而销毁,就减轻了垃圾回收的压力。
  2. 同步消除

    如果发现某个对象只能从一个线程可访问,那么在这个对象上的操作可以不需要同步。
  3. 标量替换

    Java虚拟机中的原始数据类型(int,long等数值类型以及reference类型等)都不能再进一步分解,它们就可以称为标量。相对的,如果一个数据可以继续分解,那它称为聚合量,Java中最典型的聚合量是对象。如果逃逸分析证明一个对象不会被外部访问,并且这个对象是可分解的,那程序真正执行的时候将可能不创建这个对象,而改为直接创建它的若干个被这个方法使用到的成员变量来代替。拆散后的变量便可以被单独分析与优化,可以各自分别在栈帧或寄存器上分配空间,原本的对象就无需整体分配空间了。

小结

  1. 不用担心小方法,特别是getter和setter,因为它们容易内联。
  2. 需要编译的代码在编译队列中,队列中代码越多,程序打到最佳性能的时间越久。
  3. 虽然代码缓存的大小可以调整,但它仍然是有限的资源
  4. 代码越简单,优化越多。

Java性能权威指南读书笔记--之一的更多相关文章

  1. Java性能权威指南读书笔记--之二

    新生代填满时,垃圾收集器会暂停所有的应用线程,回收新生代空间.这种操作被称为Minor GC. 老年代被填满时,垃圾收集器会暂停所有应用线程,对其进行回收,接着对堆空间进行整理.这个过程被称为Full ...

  2. 经典的性能优化最佳实践 web性能权威指南 读书笔记

    web性能权威指南 page 203 经典的性能优化最佳实践 无论什么网络,也不管所用网络协议是什么版本,所有应用都应该致力于消除或减 少不必要的网络延迟,将需要传输的数据压缩至最少.这两条标准是经典 ...

  3. 《Java性能权威指南》笔记----JIT编译器

    概览 编译型语言(C++,Fortran等):运行程序前,需要用编译器将代码静态编译成CPU可执行的汇编码.汇编码针对特定的CPU. 优点:只需编译一次,且有足够的程序信息来优化汇编码.执行速度快: ...

  4. 《Java性能权威指南》笔记----Java性能调优工具

    OS 1.CPU 用户态时间(us):cpu执行应用代码所占时间的百分比. 内核态时间(sy):cpu执行内核代码所占时间的百分比,系统态时间与应用相关. 空闲时间(id):cpu空闲时间百分比.空闲 ...

  5. HTTP权威指南读书笔记

    HTTP权威指南笔记 读书有两种境界,第一种境界是将书读薄,另一种是读厚.本篇文章就是HTTP权威指南的读书笔记,算是读书的第一重境界,将厚书读薄.文章对HTTP的一些关键概念做了比较详细的概述,通读 ...

  6. css权威指南读书笔记

    今天翻手机,翻到了许久之前看css权威指南时的笔记,遂移到博客中来. 1.属性选择器p.one class名为one的p元素p[class][name] 含有class和name属性的p元素p[cla ...

  7. css权威指南读书笔记-第10章浮动和定位

    这一章看了之后真是豁然开朗,之前虽然写了圣杯布局和双飞翼布局,有些地方也是模糊的,现在打算总结之后再写一遍. 以下都是从<css权威指南>中摘抄的我认为很有用的说明. 浮动元素 一个元素浮 ...

  8. Java性能优化权威指南-读书笔记(五)-JVM性能调优-吞吐量

    吞吐量是指,应用程序的TPS: 每秒多少次事务,QPS: 每秒多少次查询等性能指标. 吞吐量调优就是减少垃圾收集器消耗的CPU周期数,从而将更多的CPU周期用于执行应用程序. CMS吞吐调优 CMS包 ...

  9. Java性能优化权威指南-读书笔记(三)-JVM性能调优-内存占用

    新生代.老年代.永久代的概念不多说,这三个空间中任何一个不能满足内存分配请求时,就会发生垃圾收集. 新生代不满足内存分配请求时,发生Minor GC,老年代.永久代不满足内存分配请求时,发生Full ...

随机推荐

  1. java泛型的作用及其基本概念

    一.泛型的基本概念 java与c#一样,都存在泛型的概念,及类型的参数化.java中的泛型是在jdk5.0后出现的,但是java中的泛型与C#中的泛型是有本质区别的,首先从集合类型上来说,java 中 ...

  2. 从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用ApplicationPart动态加载控制器和视图

    标题:从零开始实现ASP.NET Core MVC的插件式开发(一) - 使用Application Part动态加载控制器和视图 作者:Lamond Lu 地址:http://www.cnblogs ...

  3. 与 MySQL 因“CST” 时区协商误解导致时间差了13 小时

    CST 时区名为 CST 的时区是一个很混乱的时区,有四种含义: 美国中部时间 Central Standard Time (USA) UTC-05:00 / UTC-06:00 澳大利亚中部时间 C ...

  4. JavaScript面试核心考点(精华)

    引言 Javascript是前端面试的重点,本文重点梳理下 Javascript 中的常考基础知识点,然后就一些容易出现的题目进行解析.限于文章的篇幅,无法将知识点讲解的面面俱到,本文只罗列了一些重难 ...

  5. HDU 5775:Bubble Sort(树状数组)

    http://acm.hdu.edu.cn/showproblem.php?pid=5775 Bubble Sort Problem Description   P is a permutation ...

  6. aspnetcore 实现简单的伪静态化

    aspnetcore 实现简单的伪静态化 Intro 在我的活动室预约项目中,有一个公告模块,类似于新闻发布,个人感觉像新闻这种网页基本就是发布的时候编辑一次之后就再也不会改了,最适合静态化了, 静态 ...

  7. 事务的隔离级别,mysql默认的隔离级别是什么?

    读未提交(Read uncommitted),一个事务可以读取另一个未提交事务的数据,最低级别,任何情况都无法保证. (1)所有事务都可以看到其他未提交事务的执行结果 (2)本隔离级别很少用于实际应用 ...

  8. [Python学习]错误篇二:切换当前工作目录时出错——FileNotFoundError: [WinError 3] 系统找不到指定的路径

    REFERENCE:<Head First Python> ID:我的第二篇[Python学习] BIRTHDAY:2019.7.13 EXPERIENCE_SHARING:解决切换当前工 ...

  9. CDQZ集训DAY3 日记

    早上起来之后依然开始考试.然而由于校方觉得都挨在一起没有考试氛围,分了两个机房,一开始还没人去,听说另一个机房配置好了之后一堆人开始往外冲,由于我天真的数了一下我是不是要走的,晚了一步,于是乎被教练员 ...

  10. 「PowerBI相关」一款极其优秀的DAX建模工具Tabular Editor

    做Excel插件开发的时间久了,大部分人的印象是笔者是做软件开发,谈不上软件,也是插件开发级别. 但笔者更希望大家记住的身份是做BI开发,特别是企业级的BI. 故也借着Excel催化剂这个平台,输送一 ...