关于JVM调优

转 https://www.java123.vip/wp/

备注:下图为JVM7的内存结构,JVM8的perm空间做了如下修改:Java7→Java8 Permanent→物理内存的Metaspace 后续讨论将基于JVM7的内存结构展开。

 JVM为Java运行时环境,也就是Java程序运行时的数据都由JVM来管理,如何管理好这批数据就是我们日常所说的JVM调优。

可以将JVM的存储区域作如下划分(只列举了我们要分析的区域,其他区域请自行查阅资料)

 我们来看一下下面的代码

 

我用颜色表示了上面代码在内存中存储情况。也就是局部变量存在于栈区;new的对象存在于堆区;类的定义文件存在于方法区;

 栈区

栈区的特点是快,所以局部变量存在于这块区域,供CPU在执行指令的时候直接操作。
采用入栈出栈的方式来进行空间的分配与释放,遇到左括号变量进栈,遇到右括号变量出栈。
因为变量出栈后,这块区域就被释放了,所以这个局部变量在右括号外也就无法使用了。这也解释了变量的作用域问题。

我们看到的StackOverFlow异常就是这块区域空间已满,由于遇到右括号就会释放空间,所以这块区域很少会因为单纯的一个方法内变量多导致这个异常。
一般是因为方法的递归调用层次太深,导致所有的局部变量都没有释放,继而出现StackOverFlow异常
我们可以通过设置-Xss参数来制定这块空间的大小。

 堆内存

Java程序中通过new指令在堆内存分配一块空间来存储对象数据,但是并没有把这块数据删除的可编程指令,也就是我们写程序的时候只管new对象。如果没有任何程序来清除对象的话,那么将会很快的把堆内存耗尽,报出OutOfMemoryException:heap space。

既然Java语言让我们省去了以编程的方式来管理内存可能带来的某些内存未被释放的危险,那么Java在运行时就要提供一套对不再使用的内存空间回收的方法。
我们称这个为Java的垃圾回收机制。
JVM的垃圾回收由垃圾回收器完成,可以制定不同的垃圾回收器来按不同的规则进行垃圾回收,我们可以根据需要指定最适合我们的垃圾回收器。
我们可以通过-XX参数来指定垃圾回收器。

本文中我们介绍常用分代回收算法。
把开始的图再贴一下:

Heap(堆内存)=eden+2survivor(年轻代)+ParOldGen(老生代)+Perm(jdk8以前)。jdk8以后将永久代替换为MetaSpace(元空间)存在于本地内存。from survivor 和 to survivor大小相同,且保证一个为empty。

 Heap Memory就是我们说的堆内存,存储对象,我们首先把讨论范围限定在这块区域。

一个对象从创建到消亡的过程如下:
1. 我们用new关键字创建的对象,首先出生在伊甸园,即图中的绿色部分(eden),由于他刚出生,所以他属于新生代(Young Generation)。
2. 随着程序的运行,伊甸园中的对象将会急速增长,很快这块区域将被填满,这时要保证程序继续运行,就必须清除不再使用的对象。
也就是要进行一次新生代的垃圾回收(Young GC),首先计算那些对象(假设eden和to区)不再被使用,然后还在使用中的对象我们成为幸存者。
在新生代提供了两块幸存区域(survior spaces)供幸存者使用:from和to,假设首先这部分幸存者被安排在from区(利用copy算法始终保持一个为empty,并为对象age+1.),然后eden和to区的所有对象被无情的清除,伊甸园开始继续接收新生对象。
3. 马上又到了不得不YoungGC的时间,此次检查eden和from区,幸存的对象被放入to区。
4. 不断重复2,3的过程期间一些对象经历了多次幸存游戏,我们称这些对象已经成熟,将会被移动到老年区(Old)
5. 逐渐老年区的对象也被填满,我们系统将对整个堆内存进行一次垃圾回收(full GC),这个时间明显比YoungGC的时间要长,发生的频率要小。

Java虚拟机就是不停的重复上面1,2,3,4,5的过程来维持Java世界的环境。我们通过上面的描述可以看到,想提高性能,就要尽量减少fullGC发生的频率

上图的reserved区域代表预留区域,我们先来看如何制定这几块区域的大小:
-Xmx可以指定整个堆内存的最大值。
-Xms可以指定整个堆内存的最小值,也就是初始化值,系统启动时按初始化值分配堆内存空间,内存使用超过初始值会启动两边的reserve区域,超过最大值汇报OutOfMemory:heap space异常
-XX:NewSize可以指定新生代的最小值。
-XX:MaxNewSize可以指定新生代的最大值。
老年代的最小值可以用Xms-XX:NewSize计算得出,所以不需要进行特别指定。
老年代的最大值可以用Xms-XX:MaxNewSize计算得出,所以不需要进行特别指定。

方法区(Perm Space)

这部分主要存储的类的定义,只有在进行类加载的时候才会往这个区域内增加内容,所以这个区域基本上不涉及到太多的变化因素。
在系统启动时和动态加载jar文件,class文件时,会增加这块区域的数据,如果Jar包过多,撑破这块区域则汇报OutOfMemory:perm space异常。
我们可以通过-XX:PermSize和-XX:MaxPermSize来指定这块区域的最小,最大值。

 

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的增长空间。
4)在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC ,默认为Serial收集。

5)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。

 



 

待续。。。

调优相关

 https://blog.csdn.net/chen77716/article/details/5695893

https://blog.csdn.net/u013380694/article/details/82908786

实战

https://blog.csdn.net/weixin_34327223/article/details/85600787

 https://blog.csdn.net/cml_blog/article/details/81057966

posted @ 2019-09-19 08:27  Nausicaa0505  阅读(411)  评论(0编辑  收藏  举报