性能优化系列二:JVM概念及配置
一、虚拟机组成
虚拟机主要由三部分组成:编译器(执行引擎),堆与栈。
1. 编译器
编译器分为即时编译器与解释器。
即时编译器将代码编译成本地代码存于code区。因此它快,但它有内存限制!
解释器逐行解释字节码,相当于脚本顺序执行,很慢,性能约为C语言的80%。优化的一部分是使代码尽早进入编译器。将部分代码内联(函数散开于代码中,与编译器无关)。
2. 栈
栈是JVM的函数栈。所有函数必分配于栈。栈中一个帧就是一个函数,因函数之间互相调用,栈帧中包含参数,返回地址,返回值等。第一个参数必然是this指针。递归函数会形成大量的栈帧,搞不好会溢出了。栈的大小可以配置,太大并不好。它是唯一可以配置的地方,其它就不可优化了。
3. 堆
堆是优化的重点,理解为所有对象在堆中。堆划分为年轻代,老年代,持久代(java8是metaspace-使用系统内存,而不是虚拟机堆内存,不再是perm,也就不是持久代了)。年轻代又分为幸存区两个或多个(其中一个必空)及eden(伊甸园区)。
初始的对象在伊甸园区,经过GC收集后,进入存活区,已被收集的对象当然哪也不去,被销毁了。存活区经过15次收集还存活,则进入老年代。存活区满了后交换存活区,并清空原存活区。
老年代最大,因它要放置大对象及长久不消失的对象。
从以上可以看出来堆的优化,就是合理的设置堆内存空间的大小,使之少发生GC,不浪费,不拥挤,不抖动。
JVM的参数分为-X,-XX,-D三种类型。虚拟机自身划分为客户端与server模式。我们当然要用server模式。server模式中编译器默认为混合模式,也就是即时编译器JIT与解释器混合使用。不需要改它。
二、运行原理
1. 执行引擎中的编译器,解释器执行流程
说明:
编译流程把我们编写的源代码经过词法分析器、语法分析器、语义分析器、字节码生成器处理后最终生成JVM的字节码
执行引擎的编译器和解释器获取到编译好的JVM字节码以后执行
编译器会对JVM的字节码进行机器无关的优化(如将字符串的拼接优化为append方式)、机器相关优化、寄存器分配器(将JVM字节码优化为计算机的逻辑门)存放于code区运行
解释器是一行一行的取出代码来执行,性能很慢
2. 内存分配-线程模型
说明:
在Java中一个线程就会相应有一个线程栈与之对应。
堆是所有线程共享的。
栈是运行单位,信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;
而堆只负责存储对象信息。
栈帧模型
栈帧中包含方法索引、输入输出参数、本地变量、返回地址,返回值等。第一个参数必然是this指针。递归函数会形成大量的栈帧,搞不好会溢出了。栈的大小可以配置,太大并不好。它是唯一可以配置的地方,其它就不可优化了。
三、内存管理
1. 内存分配-堆区介绍
说明:
堆是优化的重点,理解为所有对象在堆中。堆划分为年轻代,老年代,持久代(java8是metaspace-使用系统内存,而不是虚拟机堆内存,不再是perm,也就不是持久代了)。年轻代又分为幸存区两个或多个(其中一个必空)及eden(伊甸园区)。
初始的对象在伊甸园区,经过GC收集后,进入存活区,已被收集的对象当然哪也不去,被销毁了。存活区经过15次收集还存活,则进入老年代。存活区满了后交换存活区,并清空原存活区。
老年代最大,因它要放置大对象及长久不消失的对象。
注意:
Eden区满了进行的是minor GC,老年代满了进行full GC,进行full GC时整个系统会出现卡顿现象,所以堆优化的重点是合理配置堆空间,减少GC,尤其是full GC
2. 堆内存分配
Perm 默认大小64M
NewRatio配比
SurvivorRatio配比
Xmx,Xms,Xmn
四、垃圾回收策略
1. 收集器
1.Serial GC。年轻代使用的收集器,单线程,所有的线程暂停(stop the world)。一般用于Client模式的JVM中。
2.ParNew GC。年轻代使用的收集器,是在SerialGC的基础上,增加了多线程机制。
3.Parrallel Scavenge GC。年轻代使用的收集器,吞吐量优先收集器,吞吐量=程序运行时间/(JVM执行回收的时间+程序运行时间), 运行100分钟,GC占用1分钟,吞吐量=99%。server模式JVM默认配置。
4.ParallelOld。老年代使用的收集器,使用了标记整理算法,是JDK1.6中引进的。
5.Serial Old。老年代使用的收集器,CMS收集器失败后的备用收集器。
6.CMS。老年代使用的收集器,又称响应时间优先回收器,使用标记清除算法。他的回收线程数为(CPU核心数+3)/4,所以当CPU核心数为2时比较高效些。CMS分为4个过程:初始标记、并发标记、重新标记、并发清除
7. G1收集器。年轻代和老年代都使用的收集器,属于现代收集器。面向服务器- server、多CPU,多核、并行与并发、建立可预测的停顿时间模型
2. 确定垃圾-可达性分析
在主流的商用程序语言中(Java和C#),都是使用可达性分析算法判断对象是否存活的。
这个算法的基本思路就是通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
下图对象obj8, obj9, obj10它们到GC Roots是不可达的,所以它们将会判定为是可回收对象。
3. GC算法
3.1、标记-清除(Mark-Sweep)算法
标记-清除算法将垃圾回收分为两个阶段:
①.标记阶段:首先标记出所有需要回收的对象。
②.清除阶段:标记完成后,统一回收被标记的对象
缺点:
①.效率问题:标记清除过程效率都不高。
②.空间问题:标记清除之后会产生大量的不连续的内存碎片(空间碎片太多可能会导致以后在程序运行过程中需要分配较大的对象时,无法找到足够的连续的内存空间而不得不提前触发另一次垃圾收集动作。)
3.2 、标记-整理(Mark-Compact)算法
1).标记阶段:首先标记出所有需要回收的对象。与“标记-清除”一样
2).让存活的对象向内存的一段移动。而不跟“标记-清除”直接对可回收对象进行清理
3).再清理掉边界以外的内存。
由于老年代存活率高,没有额外内存对老年代进行空间担保,那么老年代只能采用标记-清理算法或者标记整理算法。
3.3 复制(Copying)算法
3.1.算法思想:
1).将现有的内存空间分为两块,每次只使用一块.
2).当其中一块用完的时候,就将还存活的对象复制到另外一块上去。
3).再把已使用过的内存空间一次清理掉。
3.2.优点:
1).由于是每次都对整个半区进行内存回收,内存分配时不必考虑内存碎片问题。
2).只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。
3.3.缺点:
1).内存减少为原来的一半,太浪费了。
2).对象存活率较高的时候就要执行较多的复制操作,效率变低。
3).如果不使用50%的对分策略,老年代需要考虑的空间担保策略。
3. 4、分代收集算法
以上三种算法的综合:
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,选用:复制算法
在老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清除”或者“标记-整理”算法来进行回收。
4. 工具
•jps
•jmap
•Jstat•Jvisualvm: window下启动远程监控,并在被监控服务端,启动jstatd服务。
4.1 jmap工具使用结果说明
命令格式:
jmap [option] <pid>
(to connect to running process)
jmap [option] <executable <core>
(to connect to a core file)
jmap [option] [server_id@]<remote server IP or hostname>
(to connect to remote debug server)
可选项:
where <option> is one of:
<none> to print same info as Solaris pmap
-heap to print java heap summary
-histo[:live] to print histogram of java object heap; if the "live"
suboption is specified, only count live objects
-clstats to print class loader statistics
-finalizerinfo to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
dump-options:
live dump only live objects; if not specified,
all objects in the heap are dumped.
format=b binary format
file=<file> dump heap to <file>
Example: jmap -dump:live,format=b,file=heap.bin <pid>
-F force. Use with -dump:<dump-options> <pid> or -histo
to force a heap dump or histogram when <pid> does not
respond. The "live" suboption is not supported
in this mode.
-h | -help to print this help message
-J<flag> to pass <flag> directly to the runtime system
使用示例:
jmap -heap 11892 其中11892是java程序的进程id
运行结果:
Heap Configuration: #堆内存初始化配置
MinHeapFreeRatio = 40 #-XX:MinHeapFreeRatio设置JVM堆最小空闲比率
MaxHeapFreeRatio = 70 #-XX:MaxHeapFreeRatio设置JVM堆最大空闲比率
MaxHeapSize = 100663296 (96.0MB) #-XX:MaxHeapSize=设置JVM堆的最大大小
NewSize = 1048576 (1.0MB) #-XX:NewSize=设置JVM堆的‘新生代’的默认大小
MaxNewSize = 4294901760 (4095.9375MB) #-XX:MaxNewSize=设置JVM堆的‘新生代’的最大大小
OldSize = 4194304 (4.0MB) #-XX:OldSize=设置JVM堆的‘老生代’的大小
NewRatio = 2 #-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
SurvivorRatio = 8 #-XX:SurvivorRatio=设置年轻代中Eden区与Survivor区的大小比值
PermSize = 12582912 (12.0MB) #-XX:PermSize=<value>:设置JVM堆的‘持久代’的初始大小
MaxPermSize = 67108864 (64.0MB) #-XX:MaxPermSize=<value>:设置JVM堆的‘持久代’的最大大小
Heap Usage:
New Generation (Eden + 1 Survivor Space): #新生代区内存分布,包含伊甸园区+1个Survivor区
capacity = 30212096 (28.8125MB)
used = 27103784 (25.848182678222656MB)
free = 3108312 (2.9643173217773438MB)
89.71169693092462% used
Eden Space: #Eden区内存分布
capacity = 26869760 (25.625MB)
used = 26869760 (25.625MB)
free = 0 (0.0MB)
100.0% used
From Space: #其中一个Survivor区的内存分布
capacity = 3342336 (3.1875MB)
used = 234024 (0.22318267822265625MB)
free = 3108312 (2.9643173217773438MB)
7.001809512867647% used
To Space: #另一个Survivor区的内存分布
capacity = 3342336 (3.1875MB)
used = 0 (0.0MB)
free = 3342336 (3.1875MB)
0.0% used
tenured generation: #当前的Old区内存分布
capacity = 67108864 (64.0MB)
used = 67108816 (63.99995422363281MB)
free = 48 (4.57763671875E-5MB)
99.99992847442627% used
Perm Generation: #当前的 “持久代” 内存分布(java8已经没有持久代的概念了)
capacity = 14417920 (13.75MB)
used = 14339216 (13.674942016601562MB)
free = 78704 (0.0750579833984375MB)
99.45412375710227% used
性能优化系列二:JVM概念及配置的更多相关文章
- 性能优化系列八:MYSQL的配置优化
一.关键配置 1. 配置文件的位置 MySQL配置文件 /etc/my.cnf 或者 /etc/my.cnf.d/server.cnf 几个关键的文件:.pid文件,记录了进程id.sock文件,是内 ...
- JVM性能优化系列-(2) 垃圾收集器与内存分配策略
2. 垃圾收集器与内存分配策略 垃圾收集(Garbage Collection, GC)是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如 ...
- JVM性能优化系列-(1) Java内存区域
1. Java内存区域 1.1 运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.主要包括:程序计数器.虚拟机栈.本地方法栈.Java堆.方法区(运 ...
- JVM性能优化系列-(3) 虚拟机执行子系统
3. 虚拟机执行子系统 3.1 Java跨平台的基础 Java刚诞生的宣传口号:一次编写,到处运行(Write Once, Run Anywhere),其中字节码是构成平台无关的基石,也是语言无关性的 ...
- JVM性能优化系列-(6) 晚期编译优化
6. 晚期编译优化 晚期编译优化主要是在运行时做的一些优化手段. 6.1 JIT编译器 在部分的商用虚拟机中,java程序最初是通过解释器(Interpreter) 进行解释执行的,当虚拟机发现某个方 ...
- JVM性能优化系列-(4) 编写高效Java程序
4. 编写高效Java程序 4.1 面向对象 构造器参数太多怎么办? 正常情况下,如果构造器参数过多,可能会考虑重写多个不同参数的构造函数,如下面的例子所示: public class FoodNor ...
- JVM性能优化系列-(5) 早期编译优化
5. 早期编译优化 早起编译优化主要指编译期进行的优化. java的编译期可能指的以下三种: 前端编译器:将.java文件变成.class文件,例如Sun的Javac.Eclipse JDT中的增量式 ...
- 推荐:Java性能优化系列集锦
Java性能问题一直困扰着广大程序员,由于平台复杂性,要定位问题,找出其根源确实很难.随着10多年Java平台的改进以及新出现的多核多处理器,Java软件的性能和扩展性已经今非昔比了.现代JVM持续演 ...
- PLSQL_性能优化系列14_Oracle High Water Level高水位分析
2014-10-04 Created By BaoXinjian 一.摘要 PLSQL_性能优化系列14_Oracle High Water Level高水位分析 高水位线好比水库中储水的水位线,用于 ...
随机推荐
- (原创)c++11改进我们的程序之垃圾回收
c#和java中有自动垃圾回收机制,.net运行时和java虚拟机可以管理分配的堆内存,在对象失去引用时自动回收,因此在c#和jva中, 内存管理不是大问题.c++语言没有垃圾回收机制,必须自己去释放 ...
- Frick'ing Terrain Mesh!
CDLOD地形的实现方法步骤: 1.实现完全二叉树结构的创建2.实现完全四叉树的视锥裁剪与LOD选择(包括节点的部分选择功能)3.使用forward rendering, 实现已选择四叉树节点的普通渲 ...
- 【消息】linux之消息队列
1.机制 消息队列的运行方式与命名管道非常相似. 欲与其他进程通信的进程只需要将消息发送到消息队列中,目的进程就从消息队列中读取需要的消息. 2.源码 1)发送方 //msg_send.c #in ...
- Upgrade Bash to 4+ on OS X
http://buddylindsey.com/upgrade-bash-to-4-on-os-x/ Unfortunately, Apple has decided to ship an old v ...
- visual studio使用GitHub
最近使用github同步项目,非常方便.以后慢慢研究版本控制的用法. visual studio使用github看这篇教程,亲测可用
- 一个实体对象不能由多个 IEntityChangeTracker 实例引用。
错误代码 public bool addSubOptionItem(csModel.cs_Answer answers) { bool result = false; wpe = new csWeiP ...
- Android 开发自己的网络收音机4——读取XML文件的电台数据
国内外的电台数据很多,起码有好几百,所以把这些数据都写到代码里面是不实际的.只能写成一个数据文件,程序启动的时候再去加载.保存这些简单数据,我们肯定会优先使用XML文件,今天讲讲如何读取XML里面的数 ...
- JQUERY的给Check全选功能
//给Checkbox提供全选功能 $("#checkall").click(function(){ if(this.checked){ $("input[name='c ...
- MyEclipse10中配置开发Python所需要的PyDev 绝对靠谱 不忽悠!
在NLP(自然语言处理)这个领域中,Python具有良好的声誉,于是也想学习一下.首先第一步就是需要在计算机上配置Python环境.由于Python自带的编辑器太简单,使用起来不顺手,于是就考虑在相对 ...
- mysql lower_case_table_names 区分表名大小写设置
Command-Line Format --lower-case-table-names[=#] System Variable Name lower_case_table_names Variabl ...