为何都在说JVM优化,如何来理解JVM的原理与如何使用优化

 

1 JVM 简单结构图

 

 
 
 

 

1.1 类加载子系统与方法区:

类加载子系统负责从文件系统或者网络中加载 Class 信息,加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中可能还会存放运行时常量池信息,包括字符串字面量和数字常量(这部分常量信息是 Class 文件中常量池部分的内存映射)。 java

1.2 Java 堆

java 堆在虚拟机启动的时候创建,它是 java 程序最主要的内存工做区域。几乎全部的 java 对象实例都存放在 java 堆中。堆空间是全部线程共享的,这是一块与 java 应用密切相关的内存空间。 linux

1.3 直接内存

java NIO 库容许 java 程序使用直接内存。直接内存是在 java 堆外的、直接向系统申请的内存空间。一般访问直接内存的速度会优于 java 堆。所以出于性能的考虑,读写频繁的场合可能会考虑使用直接内存。因为直接内存在 java 堆外,所以它的大小不会直接受限于 Xmx 指定的最大堆大小,可是系统内存是有限的,java 堆和直接内存的总和依然受限于操做系统能给出的最大内存。 程序员

1.4 垃圾回收系统

垃圾回收系统是 java 虚拟机的重要组成部分,垃圾回收器能够对方法区、java 堆和直接内存进行回收。其中,java 堆是垃圾收集器的工做重点。和 C/C++不一样,java 中全部的对象空间释放都是隐式的,也就是说,java 中没有相似 free()或者 delete()这样的函数释放指定的内存区域。对于再也不使用的垃圾对象,垃圾回收系统会在后台默默工做,默默查找、标识并释放垃圾对象,完成包括 java 堆、方法区和直接内存中的全自动化管理。 算法

1.5 Java 栈

每个 java 虚拟机线程都有一个私有的 java 栈,一个线程的 java 栈在线程建立的时候被建立,java 栈中保存着帧信息,java 栈中保存着局部变量、方法参数,同时和 java 方法的调用、返回密切相关。 windows

1.6 本地方法栈

本地方法栈和 java 栈很是相似,最大的不一样在于 java 栈用于方法的调用,而本地方法栈则用于本地方法的调用,做为对 java 虚拟机的重要扩展,java 虚拟机容许 java 直接调用本地方法(一般使用 C 编写) tomcat

1.7 PC 寄存器

PCProgram Counter)寄存器也是每个线程私有的空间,java 虚拟机会为每个 java 线程建立 PC 寄存器。在任意时刻,一个 java 线程老是在执行一个方法,这个正在被执行的方法称为当前方法。若是当前方法不是本地方法,PC 寄存器就会指向当前正在被执行的指令。若是当前方法是本地方法,那么 PC 寄存器的值就是 undefined 服务器

1.8 执行引擎

执行引擎是 java 虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提升执行效率,会使用即时编译(just in time)技术将方法编译成机器码后再执行。 网络

Java HotSpot Client VM(-client),为在客户端环境中减小启动时间而优化的执行引擎;本地应用开发使用。(如:eclipse 多线程

Java HotSpot Server VM(-server),为在服务器环境中最大化程序执行速度而设计的执行引擎。应用在服务端程序。(如:tomcat 并发

 

Java HotSpot Client 模式和 Server 模式的区别

当虚拟机运行在-client 模式的时候,使用的是一个代号为 C1 的轻量级编译器, -server 模式启动的虚拟机采用相对重量级,代号为 C2 的编译器. C2 C1 编译器编译的相对完全,服务起来以后,性能更高

JDK 安装目录/jre/lib/x86i386amd32amd64/jvm.cfg

文件中的内容,-server -client 哪个配置在上,执行引擎就是哪个。若是是 JDK1.5 版本且是 64 位系统应用时,-client 无效。

--64 位系统内容

-server KNOWN

-client IGNORE

 

--32 位系统内容

-server KNOWN

-client KNOWN

 

注意:在部分JDK1.6 版本和后续的JDK 版本(64 位系统)中,-client 参数已经不起做用

了,Server 模式成为惟一

2 堆结构及对象分代

2.1 什么是分代,分代的必要性是什么

Java 虚拟机根据对象存活的周期不一样,把堆内存划分为几块,通常分为新生代、老年代和永久代(对 HotSpot 虚拟机而言),这就是 JVM 的内存分代策略。

堆内存是虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,咱们程序全部的对象实例都存放在堆内存中。给堆内存分代是为了提升对象内存分配和垃圾回收的效率。试想一下,若是堆内存没有区域划分,全部的新建立的对象和生命周期很长的对象放在一块儿,随着程序的执行,堆内存须要频繁进行垃圾收集,而每次回收都要遍历全部的对象,遍历这些对象所花费的时间代价是巨大的,会严重影响咱们的 GC 效率。

有了内存分代,状况就不一样了,新建立的对象会在新生代中分配内存,通过屡次回收仍然存活下来的对象存放在老年代中,静态属性、类信息等存放在永久代中,新生代中的对象存活时间短,只须要在新生代区域中频繁进行 GC,老年代中对象生命周期长,内存回收的频率相对较低,不须要频繁进行回收,永久代中回收效果太差,通常不进行垃圾回收,还能够根据不一样年代的特色采用合适的垃圾收集算法。分代收集大大提高了收集效率,这些都是内存分代带来的好处。

2.2 分代的划分

Java 虚拟机将堆内存划分为新生代、老年代和永久代,永久代是 HotSpot 虚拟机特有的概念(JDK1.8 以后为 metaspace 替代永久代),它采用永久代的方式来实现方法区,其余的虚拟机实现没有这一律念,并且 HotSpot 也有取消永久代的趋势,在 JDK 1.7 HotSpot 已经开始了"去永久化",把本来放在永久代的字符串常量池移出。永久代主要存放常量、类信息、静态变量等数据,与垃圾回收关系不大,新生代和老年代是垃圾回收的主要区域。

内存简图以下:

 
 
 

 

2.2.1 新生代(Young Generation

新生成的对象优先存放在新生代中,新生代对象朝生夕死,存活率很低,在新生代中,常规应用进行一次垃圾收集通常能够回收 70% ~ 95% 的空间,回收效率很高。

HotSpot 将新生代划分为三块,一块较大的 Eden(伊甸)空间和两块较小的 Survivor(幸存者)空间,默认比例为 811。划分的目的是由于 HotSpot 采用复制算法来回收新生代,设置这个比例是为了充分利用内存空间,减小浪费。新生成的对象在 Eden 区分配(大对象除外,大对象直接进入老年代),当 Eden 区没有足够的空间进行分配时,虚拟机将发起一次

Minor GC

GC 开始时,对象只会存在于 Eden 区和 From Survivor 区,To Survivor 区是空的(做为保留区域)。GC 进行时,Eden 区中全部存活的对象都会被复制到 To Survivor 区,而在 From Survivor 区中,仍存活的对象会根据它们的年龄值决定去向,年龄值达到年龄阀值(默认为 15,新生代中的对象每熬过一轮垃圾回收,年龄值就加 1GC 分代年龄存储在对象的 header 中)的对象会被移到老年代中,没有达到阀值的对象会被复制到 To Survivor 区。接着清空 Eden 区和 From Survivor 区,新生代中存活的对象都在 To Survivor 区。接着, From Survivor 区和 To Survivor 区会交换它们的角色,也就是新的 To Survivor 区就是上次 GC 清空的 From Survivor 区,新的 From Survivor 区就是上次 GC To Survivor 区,总之,无论怎样都会保证 To Survivor 区在一轮 GC 后是空的。GC 时当 To Survivor 区没有足够的空间存放上一次新生代收集下来的存活对象时,须要依赖老年代进行分配担保,将这些对象存放在老年代中。

2.2.2 老年代(Old Generationn

在新生代中经历了屡次(具体看虚拟机配置的阀值)GC 后仍然存活下来的对象会进入老年代中。老年代中的对象生命周期较长,存活率比较高,在老年代中进行 GC 的频率相对而言较低,并且回收的速度也比较慢。

2.2.3 永久代(Permanent Generationn

永久代存储类信息、常量、静态变量、即时编译器编译后的代码等数据,对这一区域而

言,Java 虚拟机规范指出能够不进行垃圾收集,通常而言不会进行垃圾回收。

3 垃圾回收算法及分代垃圾收集器

3.1 垃圾收集器的分类

3.1.1 次收集器

Scavenge GC,指发生在新生代的 GC,由于新生代的 Java 对象大多都是朝生夕死,因此 Scavenge GC 很是频繁,通常回收速度也比较快。当 Eden 空间不足觉得对象分配内存时,会触发 Scavenge GC

通常状况下,当新对象生成,而且在 Eden 申请空间失败时,就会触发 Scavenge GC,对 Eden 区域进行 GC,清除非存活对象,而且把尚且存活的对象移动到 Survivor 区。而后整理 Survivor 的两个区。这种方式的 GC 是对年轻代的 Eden 区进行,不会影响到年老代。由于大部分对象都是从 Eden 区开始的,同时 Eden 区不会分配的很大,因此 Eden 区的 GC 会频繁进行。于是,通常在这里须要使用速度快、效率高的算法,使 Eden 去能尽快空闲出来。

当年轻代堆空间紧张时会被触发

相对于全收集而言,收集间隔较短

3.1.2 全收集器

Full GC,指发生在老年代的 GC,出现了 Full GC 通常会伴随着至少一次的 Minor GC(老年代的对象大部分是 Scavenge GC 过程当中重新生代进入老年代),好比:分配担保失败。Full GC 的速度通常会比 Scavenge GC 10 倍以上。当老年代内存不足或者显式调用 System.gc() 方法时,会触发 Full GC

当老年代或者持久代堆空间满了,会触发全收集操做可使用 System.gc()方法来显式的启动全收集

全收集通常根据堆大小的不一样,须要的时间不尽相同,但通常会比较长。

3.1.3 垃圾回收器的常规匹配

 

 
 
 

 

 

3.2 常见垃圾回收算法

3.2.1 引用计数(Reference Counting

比较古老的回收算法。原理是此对象有一个引用,即增长一个计数,删除一个引用则减小一个计数。垃圾回收时,只用收集计数为 0 的对象。此算法最致命的是没法处理循环引用的问题。

3.2.2 复制(Copying

此算法把内存空间划为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另一个区域中。此算法每次只处理正在使用中的对象,所以复制成本比较小,同时复制过去之后还能进行相应的内存整理,不会出现"碎片"问题。固然,此算法的缺点也是很明显的,就是须要两倍内存空间。简图以下:

 
 
 

 

3.2.3 标记-清除(Mark-Sweep

此算法执行分两阶段。第一阶段从引用根节点开始标记全部被引用的对象,第二阶段遍历整个堆,把未标记的对象清除。此算法须要暂停整个应用,同时,会产生内存碎片。简图以下:

 
 
 

 

3.2.4 标记-整理(Mark-Compact

此算法结合了"标记-清除"和"复制"两个算法的优势。也是分两阶段,第一阶段从根节点开始标记全部被引用对象,第二阶段遍历整个堆,把清除未标记对象而且把存活对象 "压缩"到堆的其中一块,按顺序排放。此算法避免了"标记-清除"的碎片问题,同时也避免了"复制"算法的空间问题。简图以下:

 
 
 

 

3.3 分代垃圾收集器

3.3.1 串行收集器(Serial

Serial 收集器是 Hotspot 运行在 Client 模式下的默认新生代收集器, 它的特色是:只用一个 CPU(计算核心)/一条收集线程去完成 GC 工做, 且在进行垃圾收集时必须暂停其余全部

的工做线程("Stop The World" -后面简称 STW)。可使用-XX:+UseSerialGC 打开。

虽然是单线程收集, 但它却简单而高效, VM 管理内存不大的状况下(收集几十 M~一两百 M 的新生代), 停顿时间彻底能够控制在几十毫秒~一百多毫秒内。

 
 
 

 

3.3.2 并行收集器(ParNew

ParNew 收集器实际上是前面 Serial 的多线程版本, 除使用多条线程进行 GC , 包括 Serial 可用的全部控制参数、收集算法、STW、对象分配规则、回收策略等都与 Serial 彻底同样(也是 VM 启用 CMS 收集器-XX: +UseConcMarkSweepGC 的默认新生代收集器)。因为存在线程切换的开销, ParNew 在单 CPU 的环境中比不上 Serial, 且在经过超线程技术实现的两个 CPU 的环境中也不能 100%保证能超越 Serial. 但随着可用的 CPU 数量的增长, 收集效率确定也会大大增长(ParNew 收集线程数与 CPU 的数量相同, 所以在 CPU 数量过大的环境中, 可用-XX:ParallelGCThreads=<N>参数控制 GC 线程数)

 
 
 

 

3.3.3 Parallel Scavenge 收集器

ParNew 相似, Parallel Scavenge 也是使用复制算法, 也是并行多线程收集器. 但与其余收集器关注尽量缩短垃圾收集时间不一样, Parallel Scavenge 更关注系统吞吐量:

系统吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

停顿时间越短就越适用于用户交互的程序-良好的响应速度能提高用户的体验;而高吞吐量则适用于后台运算而不须要太多交互的任务-能够最高效率地利用CPU时间,尽快地完成程序的运算任务. Parallel Scavenge 提供了以下参数设置系统吞吐量:

 

 
 
 

 

3.3.4 Serial Old 收集器

Serial Old Serial 收集器的老年代版本, 一样是单线程收集器,使用"标记-整理"算法

 
 
 

 

3.3.5 Parallel Old 收集器

Parallel Old Parallel Scavenge 收集器的老年代版本, 使用多线程和"标记-整理"算法, 吞吐量优先, 主要与 Parallel Scavenge 配合在注重吞吐量及 CPU 资源敏感系统内使用;

 
 
 

 

3.3.6 CMS 收集器(Concurrent Mark Sweep

CMS(Concurrent Mark Sweep)收集器是一款具备划时代意义的收集器, 一款真正意义上的并发收集器, 虽然如今已经有了理论意义上表现更好的 G1 收集器, 但如今主流互联网企业线上选用的还是 CMS(Taobao、微店).

CMS是一种以获取最短回收停顿时间为目标的收集器(CMS又称多并发低暂停的收集器), 基于"标记-清除"算法实现, 整个 GC 过程分为如下 4 个步骤:

1. 初始标记(CMS initial mark)

2. 并发标记(CMS concurrent mark: GC Roots Tracing 过程)

3. 从新标记(CMS remark)

4. 并发清除(CMS concurrent sweep: 已死对象将会就地释放, 注意:此处没有压缩)

其中 13 两个步骤(初始标记、从新标记)仍需 STW. 但初始标记仅只标记一下 GC Roots 能直接关联到的对象, 速度很快; 而从新标记则是为了修正并发标记期间因用户程序继续运行而致使标记产生变更的那一部分对象的标记记录, 虽然通常比初始标记阶段稍长, 但要远小于并发标记时间.

 
 
 

 

CMS 特色:

1. CMS 默认启动的回收线程数=(CPU 数目+3)4

CPU >4 , GC 线程通常占用不超过 25%CPU 资源, 可是当 CPU <=4 , GC 线程可能就会过多的占用用户 CPU 资源, 从而致使应用程序变慢, 总吞吐量下降.

 

2.没法处理浮动垃圾, 可能出现 Promotion FailureConcurrent Mode Failure 而致使另外一次 Full GC 的产生: 浮动垃圾是指在 CMS 并发清理阶段用户线程运行而产生的新垃圾. 因为在 GC 阶段用户线程还需运行, 所以还须要预留足够的内存空间给用户线程使用, 致使 CMS 不能像其余收集器那样等到老年代几乎填满了再进行收集. 所以 CMS 提供了

-XX:CMSInitiatingOccupancyFraction 参数来设置 GC 的触发百分比 ( 以及 -XX:+UseCMSInitiatingOccupancyOnly 来启用该触发百分比), 当老年代的使用空间超过该比例后 CMS 就会被触发(JDK 1.6 以后默认 92%). 但当 CMS 运行期间预留的内存没法知足程序须要, 就会出现上述 Promotion Failure 等失败, 这时 VM 将启动后备预案: 临时启用 Serial Old 收集器来从新执行Full GC(CMS一般配合大内存使用, 一旦大内存转入串行的Serial GC, 那停顿的时间就是你们都不肯看到的了).

 

3.最后, 因为 CMS 采用"标记-清除"算法实现, 可能会产生大量内存碎片. 内存碎片过多可能会致使没法分配大对象而提早触发 Full GC. 所以 CMS 提供了 -XX:+UseCMSCompactAtFullCollection 开关参数, 用于在 Full GC 后再执行一个碎片整理过程. 但内存整理是没法并发的, 内存碎片问题虽然没有了, 但停顿时间也所以变长了, 所以 CMS 还提供了另一个参数-XX:CMSFullGCsBeforeCompaction 用于设置在执行 N 次不进行内存整理的 Full GC , 跟着来一次带整理的(默认为 0: 每次进入 Full GC 时都进行碎片整理).

3.3.7 分区收集- G1 收集器

G1(Garbage-First)是一款面向服务端应用的收集器, 主要目标用于配备多颗 CPU 的服务器治理大内存.

- G1 is planned as the long term replacement for the Concurrent Mark-Sweep Collector

(CMS).

-XX:+UseG1GC 启用 G1 收集器.

与其余基于分代的收集器不一样, G1 将整个 Java 堆划分为多个大小相等的独立区域 (Region), 虽然还保留有新生代和老年代的概念, 但新生代和老年代再也不是物理隔离的了, 它们都是一部分 Region(不须要连续)的集合.如:

 
 
 

 

每块区域既有可能属于 O 区、也有多是 Y , 所以不须要一次就对整个老年代/新生代回收. 而是当线程并发寻找可回收的对象时, 有些区块包含可回收的对象要比其余区块多不少. 虽然在清理这些区块时 G1 仍然须要暂停应用线程, 但能够用相对较少的时间优先回收垃圾较多的 Region. 这种方式保证了 G1 能够在有限的时间内获取尽量高的收集效率.

G1的新生代收集跟ParNew相似: 存活的对象被转移到一个/多个Survivor Regions. 若是存活时间达到阀值, 这部分对象就会被提高到老年代.如图:

其特定是:一整块堆内存被分为多个 Regions. 存活对象被拷贝到新的 Survivor 区或老年代. 年轻代内存由一组不连续的 heap 区组成, 这种方法使得能够动态调整各代区域尺寸.

Young GC 会有 STW 事件, 进行时全部应用程序线程都会被暂停. 多线程并发 GC.

 

G1 老年代GC 特色以下: 并发标记阶段

1 在与应用程序并发执行的过程当中会计算活跃度信息.

2 这些活跃度信息标识出那些regions 最适合在STW 期间回收(which regions will be best to reclaim during an evacuation pause).

3 不像CMS 有清理阶段.

再次标记阶段

1 使用Snapshot-at-the-Beginning(SATB)算法比CMS 快得多.

2 空region 直接被回收.

拷贝/清理阶段(Copying/Cleanup Phase)

1 年轻代与老年代同时回收.

2 老年代内存回收会基于他的活跃度信息.

4 JVM 优化

4.1 JDK 经常使用 JVM 优化相关命令

 
 
 

 

 
 
 

 

4.1.1 jps

jps - l

显示线程 id 和执行线程的主类名

jps -v

显示线程 id 和执行线程的主类名和 JVM 配置信息

 
 
 

 

4.1.2 jstat

jstat -参数 线程 id 执行时间(单位毫秒) 执行次数

jstat -gc 4488 30 10

 
 
 

SXC - survivor 初始空间大小,单位字节。

SXU - survivor 使用空间大小, 单位字节。

EC - eden 初始空间大小

EU - eden 使用空间大小

OC - old 初始空间大小

OU - old 使用空间大小

PC - permanent 初始空间大小

PU - permanent 使用空间大小

YGC - youngGC 收集次数

YGCT - youngGC 收集使用时长, 单位秒

FGC - fullGC 收集次数

FGCT - fullGC 收集使用时长

GCT - 总计收集使用总时长 YGCT+FGCT

 

4.1.3 jvisualvm

一个 JDK 内置的图形化 VM 监视管理工具

 
 
 

 

4.1.4 visualgc 插件

 
 
 

 

重启 jvisualvm 工具

 
 
 

 

4.2 JVM 常见参数

配置方式:java [options] MainClass [arguments]

options - JVM 启动参数。 配置多个参数的时候,参数之间使用空格分隔。

参数命名: 常见为 -参数名

参数赋值: 常见为 -参数名=参数值 | -参数名:参数值

4.2.1 内存设置

-Xms:初始堆大小,JVM 启动的时候,给定堆空间大小。

-Xmx:最大堆大小,JVM 运行过程当中,若是初始堆空间不足的时候,最大能够扩展到多少。

-Xmn:设置年轻代大小。整个堆大小=年轻代大小+年老代大小+持久代大小。持久代通常固定大小为 64m,因此增大年轻代后,将会减少年老代大小。此值对系统性能影响较大,

Sun 官方推荐配置为整个堆的 3/8

-Xss 设置每一个线程的 Java 栈大小。JDK5.0 之后每一个线程 Java 栈大小为 1M,之前每一个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减少这个值能生成更多的线程。可是操做系统对一个进程内的线程数仍是有限制的,不能无限生成,经验值在 3000~5000 左右。

-XX:NewSize=n:设置年轻代大小

-XX:NewRatio=n:设置年轻代和年老代的比值。如:3,表示年轻代与年老代比值为 1

3,年轻代占整个年轻代+年老代和的 1/4

-XX:SurvivorRatio=n:年轻代中 Eden 区与两个 Survivor 区的比值。注意 Survivor 区有两个。

如:3,表示 EdenSurvivor=32,一个 Survivor 区占整个年轻代的 1/5

-XX:MaxPermSize=n:设置持久代大小

-XX:MaxTenuringThreshold:设置垃圾最大年龄。若是设置为 0 的话,则年轻代对象不通过 Survivor 区,直接进入年老代。对于年老代比较多的应用,能够提升效率。若是将此值设置为一个较大值,则年轻代对象会在 Survivor 区进行屡次复制,这样能够增长对象再年轻代的存活时间,增长在年轻代即被回收的几率。

 

4.2.2 内存设置经验分享

JVM 中最大堆大小有三方面限制:相关操做系统的数据模型(32-bt 仍是 64-bit)限制;系统的可用虚拟内存限制;系统的可用物理内存限制。32 位系统 下,通常限制在 1.5G~2G64 为操做系统对内存无限制。

Tomcat 配置方式: 编写catalina.bat|catalina.sh,增长JAVA_OPTS 参数设置。windows linux 配置方式不一样。windows - set "JAVA_OPTS=%JAVA_OPTS% 自定义参数"linux -

JAVA_OPTS="$JAVA_OPTS 自定义参数" 常见设置:

-Xmx3550m -Xms3550m -Xmn2g -Xss128k 适合开发过程的测试应用。要求物理内存大于 4G

-Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4

-XX:MaxPermSize=160m -XX:MaxTenuringThreshold=0 适合高并发本地测试使用。且大数据对

象相对较多(如 IO 流)

环境: 16G 物理内存,高并发服务,重量级对象中等(线程池,链接池等),经常使用对象

比例为 40%(运行过程当中产生的对象 40%是生命周期较长的)

-Xmx10G -Xms10G -Xss1M -XX:NewRatio=3 -XX:SurvivorRatio=4 -XX:MaxPermSize=2048m

-XX:MaxTenuringThreshold=5

4.2.3 收集器设置

收集器配置的时候,次收集器和全收集器必须匹配。具体匹配规则参考 3.1.3

-XX:+UseSerialGC:设置串行收集器,年轻带收集器, 次收集器

-XX:+UseParallelGC:设置并行收集器

-XX:+UseParNewGC:设置年轻代为并行收集。可与 CMS 收集同时使用。JDK5.0 以上,JVM 会根据系统配置自行设置,因此无需再设置此值。

-XX:+UseParallelOldGC:设置并行年老代收集器,JDK6.0 支持对年老代并行收集。

-XX:+UseConcMarkSweepGC:设置年老代并发收集器,测试中配置这个之后,-XX:NewRatio 的配置失效,缘由不明。因此,此时年轻代大小最好用-Xmn 设置。

-XX:+UseG1GC:设置 G1 收集器

4.2.4 垃圾回收统计信息

相似日志的配置信息。会有控制台相关信息输出。 商业项目上线的时候,不容许使用。必定使用 loggc

-XX:+PrintGC

-XX:+Printetails

-XX:+PrintGCTimeStamps

-Xloggc:filename

 

4.2.5 并行收集器设置

-XX:ParallelGCThreads=n:设置并行收集器收集时最大线程数使用的 CPU 数。并行收集线程数。

-XX:MaxGCPauseMillis=n:设置并行收集最大暂停时间,单位毫秒。能够减小 STW 时间。

-XX:GCTimeRatio=n:设置垃圾回收时间占程序运行时间的百分比。公式为 1/(1+n)并发收集器设置

-XX:+CMSIncrementalMode:设置为增量模式。适用于单 CPU 状况。

-XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动选择年轻代区大小和相应的 Survivor 区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

-XX:CMSFullGCsBeforeCompaction=n:因为并发收集器不对内存空间进行压缩、整理,因此运行一段时间之后会产生"碎片",使得运行效率下降。此值设置运行多少次 GC 之后对内存空间进行压缩、整理。

-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,可是能够消除碎片

 

4.2.6 收集器设置经验分享

关于收集器的选择 JVM 给了三种选择:串行收集器、并行收集器、并发收集器,可是

串行收集器只适用于小数据量的状况,因此这里的选择主要针对并行收集器和并发收集器。默认状况下,JDK5.0 之前都是使用串行收集器,若是想使用其余收集器须要在启动时加入相应参数。JDK5.0 之后,JVM 会根据当前系统配置进行判断。

常见配置:并行收集器主要以到达必定的吞吐量为目标,适用于科学计算和后台处理等。

-Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

使用 ParallelGC 做为并行收集器, GC 线程为 20CPU 核心数>=20 时),内存问题根据

硬件配置具体提供。建议使用物理内存的 80%左右做为 JVM 内存容量。

 

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelOldGC

指定老年代收集器,在JDK5.0以后的版本,ParallelGC对应的全收集器就是ParallelOldGC

能够忽略

 

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

指定 GC 时最大暂停时间。单位是毫秒。每次 GC 最长使用 100 毫秒。能够尽量提升

工做线程的执行资源。

 

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100

-XX:+UseAdaptiveSizePolicy

UseAdaptiveSizePolicy 是提升年轻代 GC 效率的配置。次收集器执行效率。

 

并发收集器主要是保证系统的响应时间,减小垃圾收集时的停顿时间。适用于应用服务器、电信领域、互联网领域等。

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20

-XX:+UseConcMarkSweepGC -XX:+UseParNewGC

指定年轻代收集器为 ParNew,年老代收集器 ConcurrentMarkSweep,并发 GC 线程数为 20CPU 核心>=20),并发 GC 的线程数建议使用(CPU 核心数+3/4 CPU 核心数【不推荐使用】。

 

-Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC

-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

CMSFullGCsBeforeCompaction=5 执行 5 GC 后,运行一次内存的整理。

UseCMSCompactAtFullCollection 执行老年代内存整理。能够避免内存碎片,提升 GC 过程当中的效率,减小停顿时间。

 

4.2.7 简单总结

年轻代大小选择

响应时间优先的应用:尽量设大,直到接近系统的最低响应时间限制(根据实际状况选择)。在此种状况下,年轻代收集发生的频率也是最小的。同时,减小到达年老代的对象。

吞吐量优先的应用:尽量的设置大,可能到达 Gbit 的程度。由于对响应时间没有要求,垃圾收集能够并行进行,通常适合 8CPU 以上的应用。

 

年老代大小选择

响应时间优先的应用:年老代使用并发收集器,因此其大小须要当心设置,通常要考虑并发会话率和会话持续时间等一些参数。若是堆设置小了,能够会形成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;若是堆大了,则须要较长的收集时间。

最优化的方案,通常须要参考如下数据得到:并发垃圾收集信息持久代并发收集次数

传统GC信息花在年轻代和年老代回收上的时间比例减小年轻代和年老代花费的时间,通常会提升应用的效率

吞吐量优先的应用:通常吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。缘由是,这样能够尽量回收掉大部分短时间对象,减小中期的对象,而年老代存放长期存活对象。

较小堆引发的碎片问题,由于年老代的并发收集器使用标记、清除算法,因此不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样能够分配给较大的对象。可是,当堆空间较小时,运行一段时间之后,就会出现"碎片",若是并发收集器找不到足够的空间,那么并发收集器将会中止,而后使用传统的标记、整理方式进行回收。若是出现"碎片",可能须要进行以下配置: -XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。

-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的状况下,这里设置多少次 Full GC

后,对年老代进行压缩

 

4.2.8 测试代码

 

 
 
 

此篇文章只是简单介绍一下JVM,JVM更多的资料学习获取其余IT知识学习请关注公众号:程序员理想

相关文章
相关标签/搜索