Sun JVM 内存管理、GC 工作流程、JVM 参数与调优

目录

Sun JVM 内存管理

JVM 垃圾回收工作运作流程

Sun JVM 常用参数


----

Sun JVM 内存管理

1、J2EE 系统开发的中,有时候应用系统处理的数据量不大,系统体积也不大,技术架构也没有问题,服务器的内存 8G、16G,但是
系统运行起来却很慢,还经常出现内存溢出错误。首当其冲的应该从 J2EE 应用系统本身上找问题,但是 J2EE 应用系统是运行在 J2EE 应用服务器上的,而 J2EE 应用服务器又是运行在 JVM(Java Virtual Machine)上的,所以其实在生产环境中 JVM 参数的优化和设置对 J2EE 应用系统性能有着决定性的作用。(Java SE应用也是同理)

2、本节将介绍 JAVA 的创建者 SUN 公司的 JVM 的内存管理机制(虽然除了 SUN 公司的 JVM,还有 IBM 的 JVM,Bea 的 JVM 等,但现实中绝大多数的 Java 应用是运行在 SUN 公司的 JVM 上的)。

3、JVM 的早期版本并没有进行分区管理,这样的后果是 JVM 进行垃圾回收时,不得不扫描 JVM 所管理的整片内存, 所以搜集垃圾是很耗费资源的事情,这也是早期 JAVA 程序的性能低下的主要原因。

4、随着 JVM 的发展,JVM 引进了分区管理的机制。 采用分区管理机制的 JVM 将 JVM 所管理的所有内存资源分为 2 个大的部分。 永久存储区(Permanent Space)和堆空间(The Heap Space)。其中堆空间又分为新生区(Young (New) generation space)和养老区(Tenure (Old) generation space),新生区又分为伊甸园(Eden space),幸存者0区(Survivor 0 space)和幸存者1区(Survivor 1 space)。具体分区如下图:

永久存储区(Permanent Space)
    1)永久存储区是 JVM 的驻留内存,用于存放 JDK 自身所携带的 Class , Interface 的元数据,应用服务器允许必须的 Class,Interface 的元数据和 Java 程序运行时需要的 Class 和 Interface 的元数据。
    2)被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 时,释放此区域所控制的内存。
    3)如果空间不足,则会有内存溢出错误 "永久存储区溢出(java.lang.OutOfMemoryError: Java Permanent Space)"

堆空间(The Heap Space)
    1)堆空间(The Heap Space)是 JAVA 对象生死存亡的地区,JAVA 对象的出生,成长,死亡都在这个区域完成。
    2)堆空间又分别按 JAVA 对象的创建和年龄特征分为养老区和新生区。
    3)新生区(Young (New) generation space)的作用包括 JAVA 对象的创建和从 JAVA 对象中筛选出能进入养老区的 JAVA 对象。 
    4)伊甸园(Eden space):JAVA 程序的所有对象在此出生,该区的名字因此而得名。也即是说当你的 JAVA 程序运行时,需要创建新的对象,JVM 将在该区为你创建一个指定的对象供程序使用。
    5)创建对象的依据即是永久存储区中的元数据。

     6)空间不足有JVM 堆空间溢出错误:java.lang.OutOfMemoryError: Java heap space

幸存者0区(Survivor 0 space)和幸存者1区(Survivor1 space)

    1)当伊甸园的空间用完,程序又需要创建对象时,JVM 的垃圾回收器将对伊甸园区进行垃圾回收,将伊甸园区中的不再被其他对象所引用的对象进行销毁工作。同时将伊甸园中的还有其他对象引用的对象移动到幸存者0区。
    2)幸存者0区就是用于存放伊甸园垃圾回收时所幸存下来的 JAVA 对象。当将伊甸园中的还有其他对象引用的对象移动到幸存者0区时,如果幸存者0区也没有空间来存放这些对象时,JVM 的垃圾回收器将对幸存者0区进行垃圾回收处理,将幸存者0区中不在有其他对象引用的 JAVA 对象进行销毁,将幸存者0区中还有其他对象引用的对象移动到幸存者1区。
    3)幸存者1区的作用就是用于存放幸存者0区垃圾回收处理所幸存下来的 JAVA对象。
    4)如果幸存者1区有足够空间存放则直接放到幸存者1区,如果幸存者1区没有足够空间存放,则 JVM 的垃圾回收器执行对幸存者1区的垃圾回收工作,销毁那些不再被其他对象引用的 JAVA 对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的 JAVA 对象移动到养老区。

养老区(Tenure (Old) generation space)

    1)用于保存从新生区筛选出来的 JAVA 对象。
    2、如果养老区有足够空间存放则直接放到养老区,如果养老区没有足够空间存放,则JVM的垃圾回收器执行对养老区区的垃圾回收工作,销毁那些不再被其他对象引用的JAVA对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并保留那些被其他对象所引用的JAVA对象。

JVM 垃圾回收工作运作流程

1、首先当启动 J2EE 应用服务器时,JVM 随之启动,并将 JDK 的类和接口,应用服务器运行时需要的类和接口,以及类和接口中的定义文件,还要编译后的 Class 文件或 JAR 包中的 Class 文件装载到 JVM 的永久存储区。

2、在伊甸园中创建应用服务器运行时必须的 JAVA 对象,创建 J2EE 应用启动时必须创建的JAVA对象,J2EE 应用启动完毕,可对外提供服务。 JVM 在伊甸园区根据用户的每次请求创建相应的 JAVA 对象。

3、当伊甸园的空间不足以用来创建新 JAVA 对象的时候,JVM 的垃圾回收器执行对伊甸园区的垃圾回收工作,销毁那些不再被其他对象引用的 JAVA 对象(如果该对象仅仅被一个没
有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的JAVA对象移动到幸存者0区。 

4、如果幸存者0区有足够空间存放,则直接放到幸存者0区,如果幸存者0区没有足够空间存放,则 JVM 的垃圾回收器执行对幸存者 0 区的垃圾回收工作,销毁那些不再被其他对象引用的JAVA对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的 JAVA 对象移动到幸存者1区。 

5、如果幸存者1区有足够空间存放则直接放到幸存者1区;如果幸存者1区没有足够空间存放,则JVM的垃圾回收器执行对幸存者1区的垃圾回收工作,销毁那些不再被其他对象引用的 JAVA 对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并将那些被其他对象所引用的 JAVA 对象移动到养老区。 

6、如果养老区有足够空间存放则直接放到养老区,如果养老区没有足够空间存放,则JVM的垃圾回收器执行对养老区区的垃圾回收工作,销毁那些不再被其他对象引用的JAVA对象(如果该对象仅仅被一个没有其他对象引用的对象引用的话,此对象也被归为没有存在的必要,依此类推),并保留那些被其他对象所引用的JAVA对象。

7、如果到最后养老区,幸存者1区,幸存者0区和伊甸园区都没有空间的话,则JVM会报告 "JVM堆空间溢出(java.lang.OutOfMemoryError: Java heap space)",表示在堆空间没有空间再来创建对象。 

8、这就是 JVM 的内存分区管理,相比不分区来说,一般情况下,垃圾回收的速度要快很多;因为在没有必要的时候不用扫描整片内存而节省了大量时间。 通常大家还会遇到另外一种内存溢出错误 "永久存储区溢出(java.lang.OutOfMemoryError: Java Permanent Space)"。

Sun JVM 常用参数

JVM 相关参数
参数名 参数说明
-server 启用能够执行优化的编译器, 显著提高服务器的性能,但使用能够执行优化的编译器时,服务器的预备时间将会较长。生产环境的服务器强烈推荐设置此参数。
-Xss 单个线程堆栈大小值;JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
-XX:+UseParNewGC 可用来设置年轻代为并发收集【多CPU】,如果你的服务器有多个CPU,你可以开启此参数;开启此参数,多个CPU可并发进行垃圾回收,可提高垃圾回收的速度。此参数和+UseParallelGC,-XX:ParallelGCThreads搭配使用。
+UseParallelGC 选择垃圾收集器为并行收集器此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集 。可提高系统的吞吐量。
-XX:ParallelGCThreads 年轻代并行垃圾收集的前提下(对并发也有效果)的线程数,增加并行度,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。
永久存储区相关参数
参数名 参数说明
-Xnoclassgc 每次永久存储区满了后一般GC算法在做扩展分配内存前都会触发一次FULL GC,除非设置了-Xnoclassgc.
-XX:PermSize 应用服务器启动时,永久存储区的初始内存大小
-XX:MaxPermSize 应用运行中,永久存储区的极限值。为了不消耗扩大 JVM 永久存储区分配的开销,将此参数和-XX:PermSize 这个两个值设为相等。
堆空间相关参数
参数名 参数说明
-Xms 启动应用时,JVM 堆空间的初始大小值。
-Xmx 应用运行中,JVM 堆空间的极限值。为了不消耗扩大JVM堆空间分配的开销,将此参数和-Xms这个两个值设为相等,考虑到需要开线程,将此值设置为总内存的80%.
-Xmn 此参数硬性规定堆空间的新生代空间大小,推荐设为堆空间大小的1/4。

1、JVM 参数关系到系统的性能,而其中 -XX:PermSize,-XX:MaxPermSize,-Xms,-Xmx 和 -Xmn 这 5 个参数更是直接关系到系统的性能,系统是否会出现内存溢出。

2、-XX:PermSize 和 -XX:MaxPermSize 分别设置应用服务器启动时,永久存储区的初始大小和极限大小;在生产环境中强烈推荐将这个两个值设置为相同的值,以避免分配永久存储区的开销,具体的值可取系统 "疲劳测试" 获取到的永久存储区的极限值。如果不进行设置-XX:MaxPermSize ,则默认值为 64M,一般来说系统的类定义文件大小都会超过这个默认值。 

3、-Xms 和 -Xmx 分别是服务器启动时,堆空间的初始大小和极限值。-Xms 的默认值是物理内存的 1/64 但小于1G,-Xmx 的默认值是物理内存的 1/4 但小于1G。在生产环境中这些默认值是无法满足需要的,即使服务器有 8g 的内存,如果不对 JVM 参数进行设置优化,应用服务器启动时还是按默认值来分配和约束 JVM 对内存资源的使用,不会充分的利用所有的内存资源。 

4、这也就是为什么 "服务器8g、16g内存,系统也就100M左右,数据量也不大,居然出现内存溢出" 。如:"永久存储区溢出(java.lang.OutOfMemoryError: Java Permanent Space)","JVM堆空间溢出(java.lang.OutOfMemoryError: Java heap space)"这两大溢出错误。永久存储区溢出是永久存储区设置太小,不能满足系统需要的大小,此时只需要调整 -XX:PermSize 和 -XX:MaxPermSize 这两个参数即可。JVM堆空间溢出是JVM堆空间不足,此时只需要调整 -Xms 和 -Xmx 这两个参数即可。
 

猜你喜欢

转载自blog.csdn.net/wangmx1993328/article/details/88934706
今日推荐