JVM结构、GC工做机制详解

转载于:https://blog.csdn.net/tonytfjing/article/details/44278233 java

1、JVM结构linux

根据《java虚拟机规范》规定,JVM的基本结构通常以下图所示:web




从左图可知,JVM主要包括四个部分:算法

1.类加载器(ClassLoader):在JVM启动时或者在类运行时将须要的class加载到JVM中。(右图表示了从java源文件到JVM的整个过程,可配合理解。 关于类的加载机制,能够参考http://blog.csdn.net/tonytfjing/article/details/47212291)数组


2.执行引擎:负责执行class文件中包含的字节码指令(执行引擎的工做机制,这里也不细说了,这里主要介绍JVM结构);tomcat


3.内存区(也叫运行时数据区):是在JVM运行的时候操做所分配的内存区。运行时内存区主要能够划分为5个区域,如图:服务器


方法区(Method Area):用于存储类结构信息的地方,包括常量池、静态变量、构造函数等。虽然JVM规范把方法区描述为堆的一个逻辑部分, 但它却有个别名non-heap(非堆),因此你们不要搞混淆了。方法区还包含一个运行时常量池。多线程

java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域(后面解释)。从存储的内容咱们能够很容易知道,方法区和堆是被全部java线程共享的。并发

java栈(Stack):java栈老是和线程关联在一块儿,每当建立一个线程时,JVM就会为这个线程建立一个对应的java栈。在这个java栈中又会包含多个栈帧,每运行一个方法就建立一个栈帧,用于存储局部变量表、操做栈、方法返回值等。每个方法从调用直至执行完成的过程,就对应一个栈帧在java栈中入栈到出栈的过程。因此java栈是现成私有的。app

程序计数器(PC Register):用于保存当前线程执行的内存地址。因为JVM程序是多线程执行的(线程轮流切换),因此为了保证线程切换回来后,还能恢复到原先状态,就须要一个独立的计数器,记录以前中断的地方,可见程序计数器也是线程私有的。

本地方法栈(Native Method Stack):和java栈的做用差很少,只不过是为JVM使用到的native方法服务的。


4.本地方法接口:主要是调用C或C++实现的本地方法及返回结果。



2、内存分配

我以为了解垃圾回收以前,得先了解JVM是怎么分配内存的,而后识别哪些内存是垃圾须要回收,最后才是用什么方式回收。


Java的内存分配原理与C/C++不一样,C/C++每次申请内存时都要malloc进行系统调用,而系统调用发生在内核空间,每次都要中断进行切换,这须要必定的开销,而Java虚拟机是先一次性分配一块较大的空间,而后每次new时都在该空间上进行分配和释放,减小了系统调用的次数,节省了必定的开销,这有点相似于内存池的概念;二是有了这块空间事后,如何进行分配和回收就跟GC机制有关了。


java通常内存申请有两种:静态内存和动态内存。很容易理解,编译时就可以肯定的内存就是静态内存,即内存是固定的,系统一次性分配,好比int类型变量;动态内存分配就是在程序执行时才知道要分配的存储空间大小,好比java对象的内存空间。根据上面咱们知道,java栈、程序计数器、本地方法栈都是线程私有的,线程生就生,线程灭就灭,栈中的栈帧随着方法的结束也会撤销,内存天然就跟着回收了。因此这几个区域的内存分配与回收是肯定的,咱们不须要管的。可是java堆和方法区则不同,咱们只有在程序运行期间才知道会建立哪些对象,因此这部份内存的分配和回收都是动态的。通常咱们所说的垃圾回收也是针对的这一部分。


总之Stack的内存管理是顺序分配的,并且定长,不存在内存回收问题;而Heap 则是为java对象的实例随机分配内存,不定长度,因此存在内存分配和回收的问题;


3、垃圾检测、回收算法

垃圾收集器通常必须完成两件事:检测出垃圾;回收垃圾。怎么检测出垃圾?通常有如下几种方法:


引用计数法:给一个对象添加引用计数器,每当有个地方引用它,计数器就加1;引用失效就减1。


好了,问题来了,若是我有两个对象A和B,互相引用,除此以外,没有其余任何对象引用它们,实际上这两个对象已经没法访问,便是咱们说的垃圾对象。可是互相引用,计数不为0,致使没法回收,因此还有另外一种方法:


可达性分析算法:以根集对象为起始点进行搜索,若是有对象不可达的话,便是垃圾对象。这里的根集通常包括java栈中引用的对象、方法区常良池中引用的对象


本地方法中引用的对象等。


总之,JVM在作垃圾回收的时候,会检查堆中的全部对象是否会被这些根集对象引用,不可以被引用的对象就会被垃圾收集器回收。通常回收算法也有以下几种:


1.标记-清除(Mark-sweep)

算法和名字同样,分为两个阶段:标记和清除。标记全部须要回收的对象,而后统一回收。这是最基础的算法,后续的收集算法都是基于这个算法扩展的。

不足:效率低;标记清除以后会产生大量碎片。效果图以下:



2.复制(Copying)

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


3.标记-整理(Mark-Compact)

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


(1,2,3 图文摘自 http://pengjiaheng.iteye.com/blog/520228,感谢原做者。)


4.分代收集算法

这是当前商业虚拟机经常使用的垃圾收集算法。分代的垃圾回收策略,是基于这样一个事实:不一样的对象的生命周期是不同的。所以,不一样生命周期的对象能够采起不一样的收集方式,以便提升回收效率。


为何要运用分代垃圾回收策略?在java程序运行的过程当中,会产生大量的对象,因每一个对象所能承担的职责不一样所具备的功能不一样因此也有着不同的生命周期,有的对象生命周期较长,好比Http请求中的Session对象,线程,Socket链接等;有的对象生命周期较短,好比String对象,因为其不变类的特性,有的在使用一次后便可回收。试想,在不进行对象存活时间区分的状况下,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对会很长,并且对于存活时间较长的对象进行的扫描工做等都是徒劳。所以就须要引入分治的思想,所谓分治的思想就是因地制宜,将对象进行代的划分,把不一样生命周期的对象放在不一样的代上使用不一样的垃圾回收方式。


如何划分?将对象按其生命周期的不一样划分红:年轻代(Young Generation)、年老代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,因此与java对象的回收关系不大,与回收息息相关的是年轻代和年老代。这里有个比喻很形象


“假设你是一个普通的 Java 对象,你出生在 Eden 区,在 Eden 区有许多和你差很少的小兄弟、×××妹,能够把 Eden 区当成幼儿园,在这个幼儿园里你们玩了很长时间。Eden 区不能无休止地放大家在里面,因此当年纪稍大,你就要被送到学校去上学,这里假设从小学到高中都称为 Survivor 区。开始的时候你在 Survivor 区里面划分出来的的“From”区,读到高年级了,就进了 Survivor 区的“To”区,中间因为学习成绩不稳定,还常常来回折腾。直到你 18 岁的时候,高中毕业了,该去社会上闯闯了。因而你就去了年老代,年老代里面人也不少。在年老代里,你生活了 20 年 (每次 GC 加一岁),最后寿终正寝,被 GC 回收。有一点没有提,你在年老代遇到了一个同窗,他的名字叫爱德华 (慕光之城里的帅哥吸血鬼),他以及他的家族永远不会死,那么他们就生活在永生代。”


具体区域能够经过VisualVM中的VisaulGC插件查看,如图(openjdk 1.7):



年轻代:是全部新对象产生的地方。年轻代被分为3个部分——Enden区和两个Survivor区(From和to)当Eden区被对象填满时,就会执行Minor GC。并把全部存活下来的对象转移到其中一个survivor区(假设为from区)。Minor GC一样会检查存活下来的对象,并把它们转移到另外一个survivor区(假设为to区)。这样在一段时间内,总会有一个空的survivor区。通过屡次GC周期后,仍然存活下来的对象会被转移到年老代内存空间。一般这是在年轻代有资格提高到年老代前经过设定年龄阈值来完成的。须要注意,Survivor的两个区是对称的,没前后关系,from和to是相对的。


年老代:在年轻代中经历了N次回收后仍然没有被清除的对象,就会被放到年老代中,能够说他们都是久经沙场而不亡的一代,都是生命周期较长的对象。对于年老代和永久代,就不能再采用像年轻代中那样搬移腾挪的回收算法,由于那些对于这些回收战场上的老兵来讲是小儿科。一般会在老年代内存被占满时将会触发Full GC,回收整个堆内存。


持久代:用于存放静态文件,好比java类、方法等。持久代对垃圾回收没有显著的影响。 


分代回收的效果图以下:



我这里之因此最后讲分代,是由于分代里涉及了前面几种算法。年轻代:涉及了复制算法;年老代:涉及了“标记-整理(Mark-Sweep)”的算法。



4、垃圾收集器

垃圾收集算法是内存回收的方法论,而实现这些方法论的则是垃圾收集器。不一样厂商不一样版本JVM所提供的垃圾收集器可能不一样,这里参照《深刻理解Java虚拟机》说的是JDK1.7版本Hotspot虚拟机,关于垃圾收集器有篇博文总结的不错,我就不说了,详见:http://blog.csdn.net/java2000_wl/article/details/8030172



GC 命令

一、jstat -gccause pid

监视Java堆情况,包括Eden区、两个survivor区、老年代、永久代等的容量、已用空间、GC时间合计等信息、致使上一次GC产生的缘由


eg.

jstat -gccause 28061

S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT    LGCC                 GCC

36.15   0.00  56.61  30.54  91.83  87.63    944  119.607     5    5.926  125.532 Allocation Failure   No GC


S0:年轻代中第一个survivor(幸存区)已使用的占当前容量百分比

S1:年轻代中第二个survivor(幸存区)已使用的占当前容量百分比

E:年轻代中Eden(伊甸园)已使用的占当前容量百分比

O:old代已使用的占当前容量百分比

M:元空间(Metaspace)已使用的占当前容量百分比,相似1.8以前的永久代(PermGen)

CCS:CCS表示的是Metaspace的使用率,也就是CCSU/CCSC算出来的

YGC:从应用程序启动到采样时年轻代中gc次数

YGCT:从应用程序启动到采样时年轻代中gc所用时间(s)

FGC:从应用程序启动到采样时old代(全gc)gc次数

FGCT:从应用程序启动到采样时old代(全gc)gc所用时间(s)

GCT:从应用程序启动到采样时gc用的总时间(s)

LGCC:最近一次full gc的缘由

GCC:


二、jmap -heap pid

显示Java堆详细信息,如使用哪一种回收器、参数配置、分代情况等


eg.

jmap -heap 28061


Attaching to process ID 28061, please wait...

Debugger attached successfully.

Server compiler detected.

JVM version is 25.91-b14

using thread-local object allocation.

Parallel GC with 30 thread(s)


Heap Configuration:

MinHeapFreeRatio         = 0

MaxHeapFreeRatio         = 100

MaxHeapSize              = 23622320128 (22528.0MB)

NewSize                  = 7516192768 (7168.0MB)

MaxNewSize               = 7516192768 (7168.0MB)

OldSize                  = 16106127360 (15360.0MB)

NewRatio                 = 2

SurvivorRatio            = 8

MetaspaceSize            = 21807104 (20.796875MB)

CompressedClassSpaceSize = 1073741824 (1024.0MB)

MaxMetaspaceSize         = 17592186044415 MB

G1HeapRegionSize         = 0 (0.0MB)


Heap Usage:

PS Young Generation

Eden Space:


capacity = 7132413952 (6802.0MB)

used     = 1967181000 (1876.0499954223633MB)

free     = 5165232952 (4925.950004577637MB)

27.580858503710132% used


From Space:

capacity = 190840832 (182.0MB)

used     = 29685552 (28.310348510742188MB)

free     = 161155280 (153.6896514892578MB)

15.555136544363839% used


To Space:

capacity = 191365120 (182.5MB)

used     = 0 (0.0MB)

free     = 191365120 (182.5MB)

0.0% used


PS Old Generation

capacity = 16106127360 (15360.0MB)

used     = 5054486720 (4820.334167480469MB)

free     = 11051640640 (10539.665832519531MB)

31.382383902867634% used

74399 interned Strings occupying 7907472 bytes.


三、jmap -dump:[live,]format=b,file=<filename> pid

生成Java堆转储快照。格式为:-dump:[live,]format=b,file=<filename>,其中live子

参数说明是否只dump出存活的对象

生成dump文件跟当前jvm内存大小有关,可能生成的文件会很大。


四、jmap -histo pid 显示堆中对象统计信息,包括类、实例数据、合计容量。

eg.

jmap -histo 28061

num     #instances         #bytes  class name

----------------------------------------------

1:      11410026     1362675080  [C

2:       2219154     1103383864  [B

3:      21667070      866682800  java.util.LinkedHashMap$Entry

4:      12121603      387891296  java.util.Hashtable$Entry

5:       5064770      283627120  java.security.Provider$Service

6:      10239384      245745216  java.security.Provider$ServiceKey

7:      10129191      243100584  java.lang.String

8:        856460      242869960  [Ljava.util.HashMap$Node;

9:        859192      236504032  [I

10:       3646059      145784816  [Ljava.lang.Object;

11:         20705       88713496  [Ljava.util.Hashtable$Entry;

12:       2650951       84830432  java.lang.StackTraceElement

13:       2806632       67359168  java.util.ArrayList

14:       1980684       63381888  java.util.HashMap$Node

15:        906512       43512576  java.util.HashMap

16:         76402       22543104  [Ljava.lang.StackTraceElement;

17:        231233       18500968  [S

......................................................

其中:

[C is a char[]

[S is a short[]

[I is a int[]

[B is a byte[]

[[I is a int[][]

C对象占用Heap这么多,每每跟String有关,String其内部使用final char[]数组来保存数据的


五、tomcat jvm 参数

eg.

CATALINA_OPTS="

-server

-Xms6000M

-Xmx6000M

-Xss512k

-XX:NewSize=2250M

-XX:MaxNewSize=2250M

-XX:PermSize=128M

-XX:MaxPermSize=256M

-XX:+AggressiveOpts

-XX:+UseBiasedLocking

-XX:+DisableExplicitGC

-XX:+UseParNewGC

-XX:+UseConcMarkSweepGC

-XX:MaxTenuringThreshold=31

-XX:+CMSParallelRemarkEnabled

-XX:+UseCMSCompactAtFullCollection

-XX:LargePageSizeInBytes=128m

-XX:+UseFastAccessorMethods

-XX:+UseCMSInitiatingOccupancyOnly

-Duser.timezone=Asia/Shanghai

-Djava.awt.headless=true"


eg2.

JAVA_OPTS="-server -Xms22528m -Xmx22528m -Xmn7g -Xss1024K -XX:+UseParallelGC -XX:ParallelGCThreads=30 -XX:TargetSurvivorRatio=85 -verbose:gc -Xloggc:/tmp/civp_gc_$(date +%Y%m%d%H%M%S).log -XX:+PrintGCDateStamps -XX:+PrintGCDe

tails -XX:ErrorFile=/tmp/civp_err_$(date +%Y%m%d%H%M%S).log";


-Xms:表示 Java 初始化堆的大小,-Xms 与-Xmx 设成同样的值,避免 JVM 反复从新申请内存,致使性能大起大落,默认值为物理内存的 1/64,默认(MinHeapFreeRatio参数能够调整)空余堆内存小于 40% 时,JVM 就会增大堆直到 -Xmx 的最大限制。


-Xmx:表示最大 Java 堆大小,当应用程序须要的内存超出堆的最大值时虚拟机就会提示内存溢出,而且致使应用服务崩溃,所以通常建议堆的最大值设置为可用内存的最大值的80%。如何知道个人 JVM 可以使用最大值,使用 java -Xmx512M -version 命令来进行测试,而后逐渐的增大 512 的值,若是执行正常就表示指定的内存大小可用,不然会打印错误信息,默认值为物理内存的 1/4,默认(MinHeapFreeRatio参数能够调整)空余堆内存大于 70% 时,JVM 会减小堆直到-Xms 的最小限制。


-Xss:表示每一个 Java 线程堆栈大小,JDK 5.0 之后每一个线程堆栈大小为 1M,之前每一个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整,在相同物理内存下,减少这个值能生成更多的线程,可是操做系统对一个进程内的线程数仍是有限制的,不能无限生成,经验值在 3000~5000 左右。通常小的应用, 若是栈不是很深, 应该是128k 够用的,大的应用建议使用 256k 或 512K,通常不易设置超过 1M,要否则容易出现out ofmemory。这个选项对性能影响比较大,须要严格的测试。


-XX:NewSize:设置新生代内存大小。

-XX:MaxNewSize:设置最大新生代新生代内存大小

-XX:PermSize:设置持久代内存大小

-XX:MaxPermSize:设置最大值持久代内存大小,永久代不属于堆内存,堆内存只包含新生代和老年代。

-XX:+AggressiveOpts:做用如其名(aggressive),启用这个参数,则每当 JDK 版本升级时,你的 JVM 都会使用最新加入的优化技术(若是有的话)。


-XX:+UseBiasedLocking:启用一个优化了的线程锁,咱们知道在咱们的appserver,每一个http请求就是一个线程,有的请求短有的请求长,就会有请求排队的现象,甚至还会出现线程阻塞,这个优化了的线程锁使得你的appserver内对线程处理自动进行最优调配。

-XX:+DisableExplicitGC:在 程序代码中不容许有显示的调用“System.gc()”。每次在到操做结束时手动调用 System.gc() 一下,付出的代价就是系统响应时间严重下降,就和关于 Xms,Xmx 里的解释的原理同样,这样去调用 GC 致使系统的 JVM 大起大落。


-XX:+UseConcMarkSweepGC:设置年老代为并发收集,即 CMS gc,这一特性只有 jdk1.5

后续版本才具备的功能,它使用的是 gc 估算触发和 heap 占用触发。咱们知道频频繁的 GC 会造面 JVM

的大起大落从而影响到系统的效率,所以使用了 CMS GC 后能够在 GC 次数增多的状况下,每次 GC 的响应时间却很短,好比说使用了 CMS

GC 后通过 jprofiler 的观察,GC 被触发次数很是多,而每次 GC 耗时仅为几毫秒。


-XX:+UseParNewGC:对新生代采用多线程并行回收,这样收得快,注意最新的 JVM 版本,当使用 -XX:+UseConcMarkSweepGC 时,-XX:UseParNewGC 会自动开启。所以,若是年轻代的并行 GC 不想开启,能够经过设置 -XX:-UseParNewGC 来关掉。


-XX:MaxTenuringThreshold:设置垃圾最大年龄。若是设置为0的话,则新生代对象不通过 Survivor 区,直接进入老年代。对于老年代比较多的应用(须要大量常驻内存的应用),能够提升效率。若是将此值设置为一 个较大值,则新生代对象会在 Survivor 区进行屡次复制,这样能够增长对象在新生代的存活时间,增长在新生代即被回收的几率,减小Full GC的频率,这样作能够在某种程度上提升服务稳定性。该参数只有在串行 GC 时才有效,这个值的设置是根据本地的 jprofiler 监控后获得的一个理想的值,不能一律而论原搬照抄。


-XX:+CMSParallelRemarkEnabled:在使用 UseParNewGC 的状况下,尽可能减小 mark 的时间。

-XX:+UseCMSCompactAtFullCollection:在使用 concurrent gc 的状况下,防止 memoryfragmention,对 live object 进行整理,使 memory 碎片减小。

-XX:LargePageSizeInBytes:指定 Java heap 的分页页面大小,内存页的大小不可设置过大, 会影响 Perm 的大小。

-XX:+UseFastAccessorMethods:使用 get,set 方法转成本地代码,原始类型的快速优化。

-XX:+UseCMSInitiatingOccupancyOnly:只有在 oldgeneration 在使用了初始化的比例后 concurrent collector 启动收集。

-Duser.timezone=Asia/Shanghai:设置用户所在时区。


-Djava.awt.headless=true:这个参数通常咱们都是放在最后使用的,这全参数的做用是这样的,有时咱们会在咱们的 J2EE 工程中使用一些图表工具如:jfreechart,用于在 web 网页输出 GIF/JPG 等流,在 winodws 环境下,通常咱们的 app server 在输出图形时不会碰到什么问题,可是在linux/unix 环境下常常会碰到一个 exception 致使你在 winodws 开发环境下图片显示的好好但是在 linux/unix 下却显示不出来,所以加上这个参数以避免避这样的状况出现。

-

Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space)。与 jmap -heap 中显示的 New gen 是不一样的。整个堆大小 = 新生代大小 + 老生代大小 + 永久代大小。在保证堆大小不变的状况下,增大新生代后,将会减少老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的 3/8。


-XX:CMSInitiatingOccupancyFraction:当堆满以后,并行收集器便开始进行垃圾收集,例如,当没有足够的空间来容纳新分配或提高的对象。对于 CMS 收集器,长时间等待是不可取的,由于在并发垃圾收集期间应用持续在运行(而且分配对象)。所以,为了在应用程序使用完内存以前完成垃圾收集周期,CMS 收集器要比并行收集器更先启动。由于不一样的应用会有不一样对象分配模式,JVM 会收集实际的对象分配(和释放)的运行时数据,而且分析这些数据,来决定何时启动一次 CMS 垃圾收集周期。这个参数设置有很大技巧,基本上知足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100 >= Xmn 就不会出现 promotion failed。例如在应用中 Xmx 是6000,Xmn 是 512,那么 Xmx-Xmn 是 5488M,也就是老年代有 5488M,CMSInitiatingOccupancyFraction=90 说明老年代到 90% 满的时候开始执行对老年代的并发垃圾回收(CMS),这时还 剩 10% 的空间是 5488*10% = 548M,因此即便 Xmn(也就是新生代共512M)里全部对象都搬到老年代里,548M 的空间也足够了,因此只要知足上面的公式,就不会出现垃圾回收时的 promotion failed,所以这个参数的设置必须与 Xmn 关联在一块儿。


-XX:+CMSIncrementalMode:该标志将开启 CMS 收集器的增量模式。增量模式常常暂停 CMS 过程,以便对应用程序线程做出彻底的让步。所以,收集器将花更长的时间完成整个收集周期。所以,只有经过测试后发现正常 CMS 周期对应用程序线程干扰太大时,才应该使用增量模式。因为现代服务器有足够的处理器来适应并发的垃圾收集,因此这种状况发生得不多,用于但 CPU状况。

-XX:NewRatio:年轻代(包括 Eden 和两个 Survivor 区)与年老代的比值(除去持久代),-XX:NewRatio=4 表示年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5,Xms=Xmx 而且设置了 Xmn 的状况下,该参数不须要进行设置。

-XX:SurvivorRatio:Eden 区与 Survivor 区的大小比值,设置为 8,表示 2 个 Survivor 区(JVM 堆内存年轻代中默认有 2 个大小相等的 Survivor 区)与 1 个 Eden 区的比值为 2:8,即 1 个 Survivor 区占整个年轻代大小的 1/10。

-XX:+UseSerialGC:设置串行收集器。

-XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。

-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集,JDK6.0 开始支持对年老代并行收集。


-XX:ConcGCThreads:早期 JVM 版本也叫-XX:ParallelCMSThreads,定义并发 CMS 过程运行时的线程数。好比 value=4 意味着 CMS 周期的全部阶段都以 4 个线程来执行。尽管更多的线程会加快并发 CMS 过程,但其也会带来额外的同步开销。所以,对于特定的应用程序,应该经过测试来判断增长 CMS 线程数是否真的可以带来性能的提高。若是还标志未设置,JVM 会根据并行收集器中的 -XX:ParallelGCThreads 参数的值来计算出默认的并行 CMS 线程数。

-XX:ParallelGCThreads:配置并行收集器的线程数,即:同时有多少个线程一块儿进行垃圾回收,此值建议配置与 CPU 数目相等。

-XX:OldSize:设置 JVM 启动分配的老年代内存大小,相似于新生代内存的初始大小 -XX:NewSize。


以上就是一些经常使用的配置参数,有些参数是能够被替代的,配置思路须要考虑的是 Java 提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度和应用有关,应该经过分析实际的垃圾收集的时间和频率来调整。假如堆的大小很大,那么彻底垃圾收集就会很慢,可是频度会下降。假如您把堆的大小和内存的须要一致,彻底收集就很快,可是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为确保最好的性能,要把堆的大小设大,确保垃圾收集不在整个基准测试的过程当中出现。

假如系统花费不少的时间收集垃圾,请减少堆大小。一次彻底的垃圾收集应该不超过 3-5 秒。假如垃圾收集成为瓶颈,那么须要指定代的大小,检查垃圾收集的周详输出,研究垃圾收集参数对性能的影响。当增长处理器时,记得增长内存,由于分配可以并行进行,而垃圾收集不是并行的。

相关文章
相关标签/搜索