java jvm 记录(三)

简介
HotSpot JVM中,和性能相关的三个部分为:Heap, JIT Compiler, Garbage Collector。

java jvm 记录(三)_第1张图片

其中Heap是数据对象存储区域,由Garbage Collector来管理。

大多数的调优选项都是通过设定合适的Garbage Collector来调整Heap区域大小来进行调优的。

性能的两个衡量指标:
1. 响应时间(Responsiveness)
2. 吞吐量(Throughput)


Garbage Collection的简介

垃圾回收的过程,是在内存中区分出正在使用的对象和不再使用的对象,然后把不用的对象删除的过程。具体步骤如下:

1. 标记(Marking), 区别出哪些内存区别在使用,哪些不在使用。文献1中有图片. 以引用计数为例, 找出被引用的对象,和没有被引用的对象. 这个过程耗时比较长.
2. 普通删除(Normal Deletion), 直接删除掉没有被引用的对象. 会引起内存碎片.
2a. 归整删除(Deletion with Compacting), 删除掉没有被引用的对象后, 并且把剩下的被引用的对象移动整理到一起. 这样会使新内容的分配更容易和更快.

分代回收
由于标识和归整的效率不高, 当分配的内存对象越来越多时, 垃圾回收的次数和时间也会迅速增长. 然而,经验表明,大多数的对象都是临时对象,生存时间很短(尽量少new对象, 如果必须不断的new对象,也可以使用thread local方式来建立对象).

所以JVM对Heap内存进行了分代: 年轻代(Young Generation), 年老代(Old Generation)和永久代(Permanent Generation).

java jvm 记录(三)_第2张图片

所有新的对象都是年轻代中分配. 当年轻代满了时, 触发小的垃圾回收过程(minor garbage collection). 在这个过程中, 不用的对象会被回收掉, 而留下的对象会被标记上年龄(age),根据年龄情况,来最终移动到年老代中.

应用线程暂停事件(Stop the World Event), 所有小的垃圾回收过程均是应用线程暂停事件, 直到垃圾回收完成后结束.

年老代存储生存时间较长的对象. 当年轻代中的对象年龄达到阈值时, 就会进行年老代. 年老代中的对象最终也需要进行垃圾回收的,会在大的垃圾回收过程(major garbage collection, 即Full GC)中进行回收.

大的垃圾回收过程也是应用线程暂停事件. 由于大的垃圾回收过程包含所有对象会更慢, 所以对响应时间要求高的应用中,应该尽量减少大的垃圾回收次数. 需要注意的是,大的垃圾回收过程中,线程暂停事件的长短,是受到年老代的垃圾回收算法影响的.

永久代包含类和方法的元信息, 在运行时由JVM来控制. 此外,Java SE库中的类和方法也是在永久代存储的.

当类和方法不再使用的时候, 会被JVM回收(unloaded). 这个回收过程包含在Full Garbage Collection中.

一般的垃圾回收过程
1. 最开始, 所有的新对象在Eden区分配, 两个survivor区(from和to区,或者称为S0和S1区)均为空.
2. 当Eden区分配满时, 触发minor GC(minor garbage collection). 被引用的对象会移动到第一个survivor区, 没有被引用的对象在Eden区清空的时候会被删除掉. (Copying回收过程)
3. 在下一个minor GC发生时, Eden区的情况还是一样的, 没有被引用的对象在Eden区清空的时候会被删除掉, 被引用的对象会移动到survivor区. 但是, 被引用的对象移动到的是第二个survivor区(S1). 此外, 之间minor GC时在S0中的对象的age会增加, 然后也移动到S1区. 当所有的对象均移动到了S1中时, S0和Eden区会被清空. 需要注意的是, 这时候, S1区中不同的对象的年龄有可能是不同的了.

空间比例:Eden:S0==8:1。设定方法:-XX:SurvivorRatio=8

4. 在下一个minor GC中,会发生同样的过程, 只是两个survivor区的位置会对调下. 被引用的对象会被移动到S0中,同时生存下来的对象的年龄会增加. Eden和S1会清空.

5. 在下一个minor GC中, 当对象的年龄达到指定条件时, 会从年轻代进入年老代(Tenured区, 终身区. 年老代只有这一个区).

最大年龄阈值设定:-XX:MaxTenuringThreshold

大对象(比如大的数组)直接进入老年代。阈值设定:-XX:PretenureSizeThreshold

6. 当minor GC次数越来越多时, 会有越来越多的对象进入年老代.

7. 最终, 年老代会发生一个major GC, 同时的年老代进行归整. (注: major GC即 Full GC)

Java GC参数
选项 描述 示例
-Xms heap值的初始值 -Xms10m, -Xms10240k
-Xmx heap值的最大值 -Xmx80m, -Xmx81920k, -Xmx83886080
-Xmn 年轻代(Young Generation)的值
-XX:PermSize 永久代(Permanet Generation)的起始值
-XX:MaxPermSize 永久代(Permanet Generation)的最大值


串行GC
Java SE5和6中, client模式默认使用的GC方式是串行GC(Serial GC). 在此模式下, minor GC和minor GC均是使用单一个虚拟CPU来串行执行的, 使用mark-compact回收算法. 这种回收算法会把老的内存移动到heap的开头, 来加快分配新内存的效率.


适用情况:

串行GC适合于client模式的大多数应用, 这些应用不需要很短的响应时间要求. 对于一个几百MBs的内存heap来说,整个Full GC过程约需要几秒钟.

串行GC适用的另一个场景中,一台机器上有很多JVM应用,超过了机器CPU核心数. 在这种情况下, 一个JVM在GC时,最好使用单一虚拟的CPU来进行,可以最大程序的避免对其它JVM的影响. 虽然这可能会导致GC时间延长, 但这是最平衡的做法.

对于小内存和CPU核心的嵌入式设备来讲,串行GC是最优的做法.

命令行参数:-XX:+UseSerialGC
例子: -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseSerialGC

并行GC
并行GC(Parallel GC)使用多线程来对年轻代进行GC. 当一个机器有N个CPU核心时,默认会使用N个GC线程. 可以使用-XX:ParallelGCThreads=<desired number>来执行GC线程数.

对于一个单CPU的机器, 即使指定了并行GC,也会使用默认的串行GC方式.
对于一个双CPU的机器, 会使用一般的并行GC.
对于更多的CPU核心的机器, 年轻代GC的时间会显著缩短.

适用情况:
并行GC会使用多CPU来增加应用的吞吐量. 当GC操作比较多并且长的等待时间可以接受时, 推荐使用并行GC. 例如, 批量打印报表, 或者大量的数据库查询.

命令行参数:-XX:+UseParallelGC
例子: -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelGC

命令行参数:-XX:+UseParallelOldGC
使用上面的参数时,不仅年轻代会并行GC, 年老代也会并行GC. 同样, 这个并行GC也是归整GC(compat GC). HotSpot仅仅在年老代中使用归整GC. 在年轻代中, 会进行Copy GC, 不需要归整.

例子: -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseParallelOldGC


Concurrent Mark Sweep (CMS)算法
并发Mark Sweep算法, 也叫并发低暂停算法(Concurrent low pause collector), 用来对tenured区进行垃圾回收. 这种算法会通过并发GC的方式来尽可能的减少暂停时间, 因此通常不会copying和compat生存下来的对象. 如果遇到了内存碎片问题,会去申请新的系统内存. 

注:对于年轻代, CMS算法使用与并行GC算法的同样的操作.

适用情况:
CMS算法适用于要求很低的暂停时间的应用, 同时可以和GC收集器共享资源. 如桌面UI应用, web服务器, 或者数据库查询服务.

命令行参数: -XX:+UseConcMarkSweepGC
线程数: -XX:ParallelCMSThreads=<n>

例子: -Xmx12m -Xms3m -Xmn1m -XX:PermSize=20m -XX:MaxPermSize=20m -XX:+UseConcMarkSweepGC -XX:ParallelCMSThreads=2


G1 GC算法

在java 7中,首次出现了G1算法(也叫Garbage First算法). 这种算法的目标是替换CMS算法. G1算法是一种并行,并发,逐步的归整,低暂停的垃圾回收算法. 与上面的其它GC算法有着显著的区别.

jdk 1.6update14及以后也有GC算法. 详细的见参考文献2.

命令行参数: -XX:+UseG1GC
例子: -Xmx12m -Xms3m -XX:+UseG1GC -jar




参考文献:
1. Java Garbage Collection Basics
2. JDK7新特性之G1 GC

你可能感兴趣的:(java)