图解Java自动内存管理机制及JVM优化配置
Java与C++最重要的区别就是内存动态分配和垃圾收集技术。对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不需要为每一个new操作去写配对的delete/free,不容易出现内存泄漏和内存溢出错误,看起来由虚拟机管理内存这一切都很美好。但是也正是因为Java程序员把内存控制权交给了JVM,一旦出现内存泄漏或内存溢出,如果不了解JVM内存使用机制,那排查错误将会成为一件异常艰难的事情。下面为大家介绍JVM内存管理机制以及JVM的优化配置。
JVM运行时内存数据区域划分:
各数据区域说明:
Java对象在内存中的访问
Object object = new Object();
Object object 存储在虚拟机栈的本地变量表中,作为一个引用reference类型数据存在。new Object() 存储在堆中,形成了一块存储了Object类型所有的实例数据的结构化内存。对象的访问实际就是通过reference访问到Java堆中的实例数据。reference访问对象有两种方式:
垃圾回收(GC)
Java内存运行时区域中程序计数器、虚拟机栈、本地方法栈三个区域,生命周期与线程相同,随着线程的开始和结束而创建和销毁;而堆和方法区(包括运行时常量池)则不一样,必须等到程序运行期间才能知道会创建哪些对象,其内存的分配和回收都是动态进行的。对于这部分的对象,JVM是如何判断对象是可回收的呢?一般有两种方法,如下图:
搜索算法是主流的方法,一般的面向对象语言都是使用此算法,比如Java、C#等;
判断对象是可回收的之后,是如何进行垃圾回收的呢?以下是各种垃圾收集算法介绍,如下图所示:
Java中使用的垃圾收集算法是分代收集算法,其内存堆空间划分如下:
从新生代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC(一般也成为Young GC)。对老年代进行的回收称为Major GC,但由于Major GC除并发GC外均需对整个堆空间—包括新生代和老年代清理,故又称为Full GC。由于Full GC时程序会暂停响应,如果JVM频繁的进行Full GC将使Java程序性能严重下降,说明此时JVM内存分配上肯定存在问题,需要进行JVM调优。
JDK1.6中提供的不同内存分代的收集器,两个收集器之间存在连线的话就说明可以搭配使用,如下图所示:
垃圾收集器的特点、作用内存分代及如何使用的说明如下图:
上介绍了Java的自动内存管理机制,包括垃圾收集算法,收集器等,那么如何进行JVM调优呢?首先来看常用的JVM配置,如下图:
小编常用的典型的JVM优化配置如下:
-Xmx:设置JVM最大可用内存为4G;
-Xms:设置JVM初始内存为4G,一般设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存;
-Xmn1600M:设置新生代大小为1600M。整个JVM内存=新生代+老年代+持久代。持久代一般固定大小为64M,所以增大新生代后,将会减小老年代大小。此值对系统性能影响较大,官方推荐配置为整个堆的3/8;
-XX:PermSize=512M:设置持久代初始大小为512M。Java8中已经取消的了此参数,如配置了此参数,会产生告警ignoring option;
-XX:MaxPermSize=512M:设置持久代最大允许分配大小为512M。Java8中已经取消的了此参数,如配置了此参数,会产生告警ignoring option;
-XX:SurvivorRatio=20:设置新生代中Eden区与Survivor区的大小比值。设置为20,则两个Survivor区与一个Eden区的比值为2:20,一个Survivor区占整个新生代的1/22;
-XX:MaxTenuringThreshold=5:在新生代中对象存活次数(经过Minor GC的次数)后仍然存活,就会晋升到老年代。如果设置为0的话,则新生代对象不经过Survivor区,直接进入老年代。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则新生代对象会在Survivor区进行多次复制,这样可以增加对象再新生代的存活时间,增加在新生代即被回收的概率;
-XX:+UseParNewGC:在新生代中使用为多线程(ParNew)收集器;
-XX:+UseConcMarkSweepGC:在老年代中使用并发收集CMS(ConcurrentMark Sweep)收集器;
-XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生”碎片”,使得运行效率降低。此参数设置运行n次FullGC以后对内存空间进行压缩、整理;
-XX:+UseCMSCompactAtFullCollection:打开对老年代的压缩。可能会影响性能,但是可以消除内存碎片;
-XX:CMSInitiatingOccupancyFraction=85:表示老年代空间到85%时就开始执行CMS,确保老年代有足够的空间接纳来自新生代的对象;
-XX:SoftRefLRUPolicyMSPerMB=0:”软引用”的对象在最后一次被访问后能存活0毫秒(默认为1秒);
-XX:+CMSClassUnloadingEnabled:相对于并行收集器,CMS收集器默认不会对持久代进行垃圾回收。如果希望对持久代进行垃圾回收,需要设置此参数;
-XX:-CMSParallelRemarkEnabled:手动配置开启并行标记,节省新生代标记时间,JDK1.6以前不需要配置,默认开启;
-XX:-ReduceInitialCardMarks:取消Card-Marking优化。
以上典型配置下Java内存情况,使用jmap -heap *(*表示实际的Java应用进程号)可以查看内存使用情况,如下示例: