JVM内存的调优
一 .JVM 内存的设置的原理
默认的 java 虚拟机的大小比较小,在对大数据进行处理时 java 就会报错: java.lang.OutOfMemoryError 。设置 jvm 内存的方法,对于单独的 .class ,可以用下面的方法对 Test 运行时的 jvm 内存进行设置。
java -Xms64m -Xmx256m Test
-Xms 是设置内存初始化的大小
-Xmx 是设置最大能够使用内存的大小(最好不要超过物理内存大小)
在 weblogic 中,可以在 startweblogic.cmd 中对每个 domain 虚拟内存的大小进行设置,默认的设置是在 commEnv.cmd 里面。
-vmargs
-Xms128M
-Xmx512M
-XX:PermSize=64M
-XX:MaxPermSize=128M
下面是这几个设置的一些背景知识:
按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中。
JVM初始分配的内存由-Xms 指定,默认是物理内存的1/64;JVM最 大分配的内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时, JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC 后调整堆的大小。
JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。
首先JVM内存首先受限于实际的最大物理内存,假设物理内存无限 大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是 2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了
JVM 内存的调优
1. Heap 设定与垃圾回收 Java Heap 分为 3 个区, Young , Old 和 Permanent 。 Young 保存刚实例化的对象。当该区被填满时, GC 会将对象移到 Old 区。 Permanent 区则负责保存反射对象,本文不讨论该区。 JVM 的 Heap 分配可以使用 -X 参数设定,
-Xms | 初始 Heap 大小 |
-Xmx | java heap 最大值 |
-Xmn | young generation 的 heap 大小 |
JVM 有 2 个 GC 线程。第一个线程负责回收 Heap 的 Young 区。第二个线程在 Heap 不足时,遍历 Heap ,将 Young 区升级为 Older 区。 Older 区的大小等于 -Xmx 减去 -Xmn ,不能将 -Xms 的值设的过大,因为第二个线程被迫运行会降低 JVM 的性能。
为什么一些程序频繁发生 GC ?有如下原因:
l 程序内调用了 System.gc() 或 Runtime.gc() 。
l 一些中间件软件调用自己的 GC 方法,此时需要设置参数禁止这些 GC 。
l Java 的 Heap 太小,一般默认的 Heap 值都很小。
l 频繁实例化对象, Release 对象。此时尽量保存并重用对象,例如使用 StringBuffer() 和 String() 。
如果你发现每次 GC 后, Heap 的剩余空间会是总空间的 50% ,这表示你的 Heap 处于健康状态。许多 Server 端的 Java 程序每次 GC 后最好能有 65% 的剩余空间。经验之谈:
1 . Server 端 JVM 最好将 -Xms 和 -Xmx 设为相同值。为了优化 GC ,最好让 -Xmn 值约等于 -Xmx 的 1/3[2] 。
2 .一个 GUI 程序最好是每 10 到 20 秒间运行一次 GC ,每次在半秒之内完成 [2] 。
注意:
1 .增加 Heap 的大小虽然会降低 GC 的频率,但也增加了每次 GC 的时间。并且 GC 运行时,所有的用户线程将暂停,也就是 GC 期间, Java 应用程序不做任何工作。
2 . Heap 大小并不决定进程的内存使用量。进程的内存使用量要大于 -Xmx 定义的值,因为 Java 为其他任务分配内存,例如每个线程的 Stack 等。
2 . Stack 的设定
每个线程都有他自己的 Stack 。
-Xss | 每个线程的 Stack 大小 |
Stack 的大小限制着线程的数量。如果 Stack 过大就好导致内存溢漏。 -Xss 参数决定 Stack 大小,例如 -Xss1024K 。如果 Stack 太小,也会导致 Stack 溢漏。
3 .硬件环境
硬件环境也影响 GC 的效率,例如机器的种类,内存, swap 空间,和 CPU 的数量。
如果你的程序需要频繁创建很多 transient 对象,会导致 JVM 频繁 GC 。这种情况你可以增加机器的内存,来减少 Swap 空间的使用 [2] 。
4 . 4 种 GC
第一种为单线程 GC ,也是默认的 GC 。,该 GC 适用于单 CPU 机器。
第二种为 Throughput GC ,是多线程的 GC ,适用于多 CPU ,使用大量线程的程序。第二种 GC 与第一种 GC 相似,不同在于 GC 在收集 Young 区是多线程的,但在 Old 区和第一种一样,仍然采用单线程。 -XX:+UseParallelGC 参数启动该 GC 。
第三种为 Concurrent Low Pause GC ,类似于第一种,适用于多 CPU ,并要求缩短因 GC 造成程序停滞的时间。这种 GC 可以在 Old 区的回收同时,运行应用程序。 -XX:+UseConcMarkSweepGC 参数启动该 GC 。
第四种为 Incremental Low Pause GC ,适用于要求缩短因 GC 造成程序停滞的时间。这种 GC 可以在 Young 区回收的同时,回收一部分 Old 区对象。 -Xincgc 参数启动该 GC 。
转自:http://ltp200406857.iteye.com/blog/1333864