JVM调优总结new
堆和栈的平衡
x86的机器上的 进程 最多可以使用 2048mb的内存,
该Java进程剩下的内存由, 方法区,程序计数器,虚拟机栈,本地方法栈 共同使用。
虚拟机栈使用的空间 = 2048- Xmx(最大堆容量)-MaxPermSize (最大方法区容量) - 本地方法栈
我们知道,一个线程对应一个虚拟机栈,
所以总结:那么Heap 越大可以供,程序申请的内存空间越少,就是说虚拟机栈越少(线程数量越少)
堆内存存储了对象,我们称为GC堆,我们增加-Xmx 只是增加了GC堆的大小正真执行程序的内存空间反而小了
- 对于高并发,创建对象不多的项目,可以降低Xmx的配置, 结合Xms 设定堆范围 -Xms256m -Xmx512
- 对于低并发,创建对象多的项目,(数据处理型的) 可以适当提高,Xmx
-Xmx 指定java程序的最大堆内存
-Xms 指定初始堆内存, 通常设置成跟最大堆内存一样,减少GC 为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间
线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般128K就够用了超过256K考虑优化。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
1.8 优先使用G1
分析前查看gc log
接着查看 gc log,打印 gc log 需要在 JVM 启动参数里添加以下参数:
-XX:+PrintGCDateStamps
:打印 gc 发生的时间戳。-XX:+PrintTenuringDistribution
:打印 gc 发生时的分代信息。-XX:+PrintGCApplicationStoppedTime
:打印 gc 停顿时长-XX:+PrintGCApplicationConcurrentTime
:打印 gc 间隔的服务运行时长-XX:+PrintGCDetails
:打印 gc 详情,包括 gc 前/内存等。-Xloggc:../gclogs/gc.log.date
:指定 gc log 的路径
偏向锁停顿
还有一个问题是 gc log 里有很多 18ms 左右的停顿,有时候连续有十多条,虽然每次停顿时长不长,但连续多次累积的时间也非常可观。
1.8 之后 JVM 对锁进行了优化,添加了偏向锁的概念,避免了很多不必要的加锁操作,但偏向锁一旦遇到锁竞争,取消锁需要进入 safe point
,导致 STW。
解决方式很简单,JVM 启动参数里添加 -XX:-UseBiasedLocking
即可。
JVM常见的调优参数
-Xmx 指定java程序的最大堆内存
-Xms 指定初始堆内存, 通常设置成跟最大堆内存一样,减少GC
-Xmn 设置年轻代大小。整个堆大小=年轻代大小 + 老年代大小。所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss 指定线程的最大栈空间, 此参数决定了java函数调用的深度, 值越大调用深度越深, 若值太小则容易出栈溢出错误(StackOverflowError)
-XX:PermSize 指定方法区(永久区)的初始值,默认是物理内存的1/64, 在Java8永久区移除, 代之的是元数据区, 由-XX:MetaspaceSize指定
-XX:MaxPermSize 指定方法区的最大值, 默认是物理内存的1/4, 在java8中由-XX:MaxMetaspaceSize指定元数据区的大小
-XX:NewRatio=n 老年代与年轻代的比值,-XX:NewRatio=2, 表示老年代与年轻代的比值为2:1
-XX:SurvivorRatio=n Eden区与Survivor(2个)区的大小比值,-XX:SurvivorRatio=8表示Eden区与Survivor区的大小比值是8:1:1,因为Survivor区有两个(from, to)
JVM 调优建议
通过设置我们希望达到一些目标:
- GC的时间足够的小
- GC的次数足够的少
- 发生Full GC的周期足够的长
前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。
1)针对JVM堆的设置一般,可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值。
2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小
年轻代和年老代设置多大才算合理?
更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC
更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率。
如何选择应该依赖应用程序对象生命周期的分布情况:
如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:
A.本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理。
B.通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间。
5)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。
https://www.cnblogs.com/andy-zhou/p/5327288.html#_caption_25