Java虚拟机调优

虚拟机是java程序运行的平台,是实现跨平台运行的基础,虚拟机的配置在很大的程度上也影响java程序所提供服务的性能。虚拟机调优主要为:虚拟机选项、基本参数调优、内存调优、垃圾回收机制

1、虚拟机选型
  • HotSpot VM:是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。可能HotSpot听起来很陌生,但你一定眼熟、眼熟、眼熟。
  • JRockit VM:JRockit是一款由BEA公司开发的优秀VM,它的垃圾回收器与MissionControl服务做的非常好。
  • J9 VM:是IBM开发的一个高度模块化的JVM,在许多平台上,IBM J9 VM都只能跟IBM产品一起使用
  • Zing VM:是Azul传统风格的产品,它从Sun HoSpot VM fork出来的一个高性能、定制和优化的JVM,在要求低延迟、快速预热等的场景里,Zing VM都会比HotSpot VM表现更好。

HotSpot和JRockit现都已被Oracle收购,据说JRockit的一些优秀特性将移植到HotSpot,与HotSpot集成;在现有的jdk1.8中已经看到JRockit的身影。HotSpot作为当前最流行的JVM,选择它当之无愧。

为方便描述,以下JDK、JVM统指HotSpot VM;虚拟机参数分为基本和扩展两类,在命令行中输入java 可得到基本参数列表,java X 则可得到扩展参数列表。

2、标准参数调优
  • -client,-server:
    用于设置虚拟机使用何种运行模式,client模式启动比较快,但运行时性能和内存管理效率不如server模式,通常用于客户端应用程序。相反,server模式启动比client慢,但可获得更高的运行性能。一般来说client选项用于gui应用程序,server选项多用于后台服务器应用。
  • -hotspot:在HotSopt类型的JVM中缺省使用,缺省为Client Hotspot模式;jdk1.4以前使用的参数,jdk1.4开始不再使用,代之以client
  • -cp:参数后面是类路径,是指定给解释器到哪里找到你的.class文件
  • -classpath:指明JVM启动时要加载的类文件路径,Java虚拟机进程在启动时就会按照该参数后面指明;同-cp
  • -verbose[:class|gc|jni]:在输出设备上显示虚拟机运行信息。
  • -D=:在虚拟机的系统属性中设置属性名/值对,运行在此虚拟机之上的应用程序可用System.getProperty(“propertyName”)得到value的值。如果value中有空格,则需要用双引号将该值括起来,如-Dname=”space string”。
3、内存调优

JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)。在hot spot虚拟机中,不区分本地方法和虚拟机栈,方法区也称为永久区。涉及到堆栈溢出或内存泄露的两个异常:StackOverflowError和OutOfMemoryError;其中OutOfMemoryError又分为:java.lang.OutOfMemoryError: Java heap space和java.lang.OutOfMemoryError: PermGen space。

堆区:
  • 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令)
  • 2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放数据
栈区:
  • 1.每个线程包含一个栈区,栈中只保存原始类型数据和对象和对象引用(不是对象),对象都存放在堆区中
  • 2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
  • 3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
方法区:
  • 1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
  • 2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。

这里写图片描述

  • Heap: JVM只有一个为所有线程所共享的堆,所有的类实例和数组都是在堆中创建的。
  • Method area: JVM只有一个为所有的线程所共享的方法区。它存储类结构,例如运行时常量池,成员和方法数据以及方法、构造方法的代码。
  • VM Stacks:每个JVM线程拥有一个私有的栈。
  • Pc registers: JVM可以同时支持运行多个线程,因此每个线程需要各自的PC(program counter)寄存器。
  • Native method stacks: 保存native方法进入区域的地址。
  • JVM运行时数据区:Heap和Method area被所有线程共享,其生存期和JVM的生存期相同;Java Stacks、Pc registers、Native method stacks被每个线程独自拥有,其生存期和线程的生存期相同。
堆内存分为三个部分:
  • 年轻代(New):年轻代用来存放JVM刚分配的Java对象。它又分为Eden区和Survivor区,默认比例为8:2。Survivor区又可以分为From Survivor和To Survivor区,其中From Survivor和To Survivor在GC的过程中是可以相互转化的。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。
  • 年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被Copy到年老代
  • 永久代(Perm):永久代存放Class、Method元信息,其大小跟项目的规模、类、方法的量有关,一般设置为128M就足够,设置原则是预留30%的空间。
内存调优相关参数
  • -Xmixed:JVM执行模式的设置参数,混合模式即支持Hotspot即时编译的运行模式;在dos窗口显示版本信息中,HotSpot VM默认就是mixed mode
  • Xint:设置JVM的执行模式为解释性执行模式
  • Xms:设置JVM启动时初始内存堆的大小
  • Xmx:设置JVM启动后动态申请堆内存的最大堆空间
  • Xss:设置JVM每个线程栈的最大空间大小
  • Xmx:设置是否启动远程调试功能
  • Xmn:设置年轻代堆内存大小,整个堆内存大小 = 年轻代大小 + 年老代大小 + 持久代大小 。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
  • -XX:PermSize:设置堆内存持久代大小
  • -XX:MaxPermSize:设置堆内存持久代可申请的最大空间
  • -XX:NewSize:设置年轻代的大小
  • -XX:MaxNewSize:设置年轻代能占用内存的最大值
  • -XX:NewRatio:设置堆内存年轻代(包括Eden和两个Survivor区)与堆内存年老代的比值
  • -XX:SurvivorRatio:设置堆内存年轻代中Eden区与Survivor区大小的比值
    -XX:MaxTenuringThreshold:设置一个对象如果在救助空间(Survivor区)移动而没有被回收就放入年老代的最大次数;如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代,对于年老代比较多的应用,这样做可以提高效率。
  • -XX:TargetSurvivorRatio:设置survivior 的使用率。当达到这个空间使用率时,会将对象送入老年代。

  • -XX:MinHeapFreeRatio:设置堆空间的最小空间比例。当堆空间的空闲内存小于这个数值时,jvm便会扩展堆空间。

  • -XX:MaxHeapFreeRatio:设置堆空间的最大空间比例。当堆空间的空闲内存大于这个数值时,jvm便会缩小堆空间。

这里涉及到JVM运行模式,所以解释一下mixed mode:

java需要编译,解释型语言不需要编译,然而java编译的结果又不是和其他编译型语言一样的二进制文件,而是class文件。从这一点上来看,java介于二者中间;java编译完成以后,操作系统不能直接运行,而是需要java虚拟机解释执行class字节码文件。因此虚拟机将字节码程序与操作系统及硬件分开,使得java程序能在异构平台上执行,从这一点上来看,java又属于解释型语言。如果严格来说,java确实属于半编译半解释型语言。

编写代码查看部分堆大小:

System.out.println(Runtime.getRuntime().maxMemory()/1024/1024); // 可申请的最大内存
System.out.println(Runtime.getRuntime().totalMemory()); // 已申请的内存
System.out.println(Runtime.getRuntime().freeMemory());  // 已申请但未使用的内存
4、垃圾收集机制设置

JVM提供了三种垃圾收集选择:串行收集器(单线程)、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。
* -XX:+UseSerialGC:设置串行收集器,

  • -XX:+UseParallelGC:设置并行收集器,并行收集器主要以到达一定的吞吐量为目标,保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
  • -XX:+UseParNewGC:设置并行年轻代收集器
  • -XX:+UseParalledlOldGC:设置并行年老代收集器
  • -XX:ParallelGCThreads:设置并行收集器收集时使用的CPU数。并行收集线程数
  • -XX:MaxGCPauseMillis:设置并行收集最大暂停时间
  • -XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比。公式为1/(1+n)

  • -XX:+UseConcMarkSweepGC:设置并发收集器

  • -XX:+CMSIncrementalMode :设置为增量模式。适用于单CPU情况。
  • -XX:ParallelGCThreads:设置并发收集器年轻代收集方式为并行收集时,使用的CPU数;并行收集线程数。
  • -XX:+CollectGen0First:FullGC时是否先YGC
  • -Xnoclassgc:禁用垃圾回收
GC与Full GC
  • GC(或Minor GC):收集 生命周期短的区域(Young area)。

  • Full GC (或Major GC):收集生命周期短的区域(Young area)和生命周期比较长的区域(Old area)对整个堆进行垃圾收集。

垃圾回收动作何时执行:

当年轻代内存满时,会引发一次普通GC,该GC仅回收年轻代。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC
当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代
当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载

Minor GC触发条件:当Eden区满时,触发Minor GC。
Full GC触发条件:
* 调用System.gc时,系统建议执行Full GC,但是不必然执行

  • 老年代空间不足

  • 方法区空间不足

  • 通过Minor GC后进入老年代的平均大小大于老年代的可用内存

  • 由Eden区、From Space区向To Space区复制时,对象大小大于To Space可用内存,则把该对象转存到老年代,且老年代的可用内存小于该对象大小

5、调优案例
参数方案:
-server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio=6 -XX:MaxPermSize=256m -XX:ParallelGCThreads=8 -XX:MaxTenuringThreshold=0 -XX:+UseConcMarkSweepGC
调优参数说明:

-Xmx 与 -Xms 相同以避免JVM反复重新申请内存。-Xmx 的大小约等于系统内存大小的一半,即充分利用系统资源,又给予系统安全运行的空间。
-Xmn1256m 设置年轻代大小为1256MB。此值对系统性能影响较大,Sun官方推荐配置年轻代大小为整个堆的3/8。
-Xss128k 设置较小的线程栈以支持创建更多的线程,支持海量访问,并提升系统性能。
-XX:SurvivorRatio=6 设置年轻代中Eden区与Survivor区的比值。系统默认是8,根据经验设置为6,则2个Survivor区与1个Eden区的比值为2:6,一个Survivor区占整个年轻代的1/8。
-XX:ParallelGCThreads=8 配置并行收集器的线程数,即同时8个线程一起进行垃圾回收。此值一般配置为与CPU数目相等。
-XX:MaxTenuringThreshold=0 设置垃圾最大年龄(在年轻代的存活次数)。如果设置为0的话,则年轻代对象不经过Survivor区直接进入年老代。对于年老代比较多的应用,可以提高效率;如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率。根据被海量访问的动态Web应用之特点,其内存要么被缓存起来以减少直接访问DB,要么被快速回收以支持高并发海量请求,因此其内存对象在年轻代存活多次意义不大,可以直接进入年老代,根据实际应用效果,在这里设置此值为0。
-XX:+UseConcMarkSweepGC 设置年老代为并发收集。CMS(ConcMarkSweepGC)收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存,适用于应用中存在比较多的长生命周期对象的情况。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值