初步了解jvm的内存分配,gc以及简单的jvm内存配置

一、jvm常见问题

1、内存不足,这里主要是指堆内存不足,会引发OOM,out of memory
2、cpu飙升,系统卡顿

二、简单了解jvm内存和gc

在此之前,我们应该对jvm的内存分配,以及基本的gc机制有一定的了解。
在这里插入图片描述
简单说,jdk8以后,堆区就分为新生代老年代,图中的Permanent永久代被移除了,用元空间代替。

默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。

老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。

默认的,Eden : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。

创建的对象首先在新生代,当新生代使用内存到一定值,触发一次minor gc,回收机会把Eden和其中一个Survivor 中存活的对象,复制到另一个空的Survivor 中,然后清空前面两个区。

新生代的Minor GC 和老年代的Full GC都会导致stop the world,就是停止所有线程

gc触发条件

1、Minor GC 触发的条件

当Eden区满的时候,会触发Minor GC

2、Full GC触发的条件

(1)调用System.gc的时候,系统建议执行Full GC,但是不是必然执行的。

    此方法只是建议使用,很多情况下会触发Full GC,从而增加GC的频率,增加了服务间歇性停顿的次数。建议能不使用此方法就别使用,让虚拟机自己去管理它的内存,通过-XX:DisableExplicitGC来禁止RMI调用System.GC

(2)老年代空间不足的时候会触发Full GC

    当新生代对象没有通过GC回收之后转入到老年代或者创建大对象,大数组的时候老年代才会出现空间不足的现象,此时会执行Full GC。如果在执行Full GC之后空间还是不足,就会抛出java.lang.OutOfMemoryError: Java heap space错误。

    为了避免上述情况出现而引起的Full GC,虚拟机调优的时候尽量做到让对象在Minor GC(新生代垃圾回收)阶段被回收,让对象在新生代多存活一段时间,不要创建过大的对象和数组。

(3)方法区空间不足的时候会触发Full GC

    方法区在JDK1.7也称为永久代,主要存放一些class的信息、常量、静态变量等数据,当系统中要加载的类、反射的类和调用的方法比较多的时候,永久代可能会被占满,在没有配置采用CMS GC的情况下也会去触发执行Full GC。如果经过Full GC之后对象仍然不能回收,那么JVM就会抛出java.lang.OutOfMemoryError: PermGen space。

    为了避免永久代(Perm Gen)被沾满触发Full GC的现象,可以采用的方法是增大永久代(Perm Gen)的空间或者转为使用CMS GC的方法。

(4)通过Minor GC后进入到老年代的平均大小大于老年代的可用内存的时候会触发Full GC

    Hotspot为了避免由于新生代对象晋升到老年代导致老年代空间不足的现象,在进行Minor GC(新生代垃圾回收)的时候,做个一个判断,如果之前统计所得到的Minor GC晋升到老年代的平均大小大于老年代的剩余的空间,那么就会触发Full GC。

    例如:当程序第一次触发Minor GC的时候,有6M的对象晋升到了老年代,那么当下一次Minor GC的时候,首先会去检查老年代剩余的空间大小是否小于6M,如果小于6M,则会去执行Full GC。

(5)由Eden区、From Space区向To Space区复制的时候,对象大小大于To Space可用内存,则把该对象转存到老年代,并且老年代的可用内存小于该对象的大小的时候会触发Full GC

(6)堆中分配很大的对象导致触发Full GC

    大对象主要是指大量连续的内存空间的java 对象,例如很长的数组,此种对象会直接进入到老年代。而老年代虽然有很大的剩余空间,但是无法找到足够大的连续空间来分配给当前对象,此种情况下就会触发JVM进行Full GC

三、JVM参数配置

上面说了jvm的内存分配和gc回收机制,那么内存溢出,OOM的原因,首先考虑是不是配置的内存不够。jvm默认配置的最大堆内存是64M,这对一个普通的项目是远远不够的。需要我们自己设置参数

3.1 参数含义

-Xmx512m									最大总堆内存
-Xms512m									初始总堆内存,一般将它设置的和最大堆内存一样大,这样就不需要根据当前堆使用情况而调整堆的大小了
-Xmn192m									新生代堆内存,sun官方推荐为整个堆的3/8
-XX:NewRatio 							老年代和新生代和的大小比值,为2表示新生代占堆区的1/3
-XX:SurvivorRatio						Eden区和一个survivor区的大小比值,为8表示一个survivor区占新生代的1/10
-XX:PermSize=128m					永久代堆的初始大小,jdk8以后永久代移除了
-XX:MaxPermSize=128m			永久代堆的最大大小,jdk8以后永久代移除了
----jdk8以后-----
-XX:MetaspaceSize					初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。 
-XX:MaxMetaspaceSize				最大空间,默认是没有限制的

3.2 tomcat设置

catalina.sh文件中,找到cygwin=false,在这一行的前面加入参数,具体如下

# vi TOMCAT_HOME/bin/catalina.sh
JAVA_OPTS="-server -Xms800m -Xmx800m -XX:PermSize=256m -XX:MaxPermSize=512m -XX:MaxNewSize=512m"

3.3 jar设置

启动的时候,在java -jar xxx.jar中加入, 如:

java -jar -Xmx512M -Xms256M -Xmn128M   zgd-web.jar

四、简单调优

简单的jvm优化配置原则

通用法则1:

将java堆的初始值-Xms和最大值-Xmx设置为老年代活跃数据大小的3~4倍

通用法则2:

永久带的初始值-XX:PermSize及最大值-XX:MaxPermSize应该比永久代活跃数据大1.2~1.5倍

补充法则:

新生代空间应该为老年代空间活跃数据的1~1.5倍

如果java堆的初始值及最大值为活跃数据的3~ 4,新生代为活跃数据的1~ 1.5倍时,老年代应设置为活跃数据大小的2~3倍

参考:《java性能优化权威指南》

这里说的活跃数据,是fullgc后的占用内存大小

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章