值得阅读的内存泄露分析总结和Tomcat调优
直接内存区并不是 JVM 管理的内存区域的一部分,而是其之外的。该区域也会在 Java 开发中使用到,并且存在导致内存溢出的隐患。如果你对 NIO 有所了解,可能会知道 NIO 是可以使用 Native Methods 来使用直接内存区的。 Part2.Heap(堆)和CMS垃圾回收算法 下面我们要详细分析一下Heap,Heap(堆)又可以细分成三部分,Old Gen(老年堆),Eden Space(年轻堆也叫伊甸园),Survivor Space(S0+S1)。我们可以通过配置参数控制Heap的大小,具体设置在后面调优会讲。当程序运行时,大多数情况new的一些对象,最开始都会存放Par Eden Space,然后多次回收(Young GC)之后仍然存活的对象就会挪到CMS Old Gen(老年堆)。需要注意的是除此之外,大的数组对象且对象中无外部引用的对象,和通过启动参数设置的-XX:PretenureSizeThreshold=1024(字节),超过这个大小的对象都会直接分配到CMS Old Gen(老年堆)。下面我们要讲的垃圾回收算法就是发生在这个地方。在我们应用环境中,由于我们配置了CMS GC(并发GC)的回收方法,所以对Eden Space使用的GC算法默认就是ParNew(并行GC)。这里供Par Eden Space和Old Gen选择的GC算法有很多种,可以根据自己的环境选择,一般多核CPU都会选择CMS(并发GC),这样更高效。 CMS执行过程可以分成:初始标记,并发标记,并发预处理,重标记,并发清理,重置六个阶段,这里需要注意的是初始标记和重标记两个阶段是需要Stop-the-world,其他阶段都是和程序其他进程并发执行的,System.gc()调用的Full GC的整个过程都是Stop-the-world,这也是为什么说CMS是对系统影响最小的垃圾回收方法。 初始标记:该阶段进行可达性分析,标记GC ROOT可以直接关联的对象。注意这里是直接关联,间接关联的将在第二阶段进行标记。那么什么可以作为GC ROOT呢,一般是:①虚拟机栈中的引用对象。②方法区中类静态属性引用的对象③方法区中常量引用对象④本地方法栈中JNI引用对象。 并发标记阶段:该阶段进行GC ROOT Tracing(大家可以把这个想象成由一个Root构成的树,树上除了Root节点,存在引用关系的其他节点到Root都有可达路径。),在第一阶段被暂停的线程全部恢复执行,然后从上一阶段mark的对象出发,对所有可达的对象进行标记。 并发预处理:这一步就是CMS算法的精髓所在,因为CMS是以获取最短的停顿时间为目的的GC算法。在mark和remark两个阶段都需要Stop-the-world,所以并发预处理的目的就是提前做一些remark做的事情,减短remark阶段的耗时。这一阶段,将标记从Eden Space晋升的对象、从Eden Space分配到Old Gen的对象,以及在并发标记阶段被修改的对象。怎么确定一个对象是否存活,即通过追踪GC ROOT Tracing有可达路径的对象就是活着的。举个例子吧,就比如说一个在Old Gen中存在对象B,在并发标记阶段没被标记成alive,眼看就要小命不保了,就在这个时候程序进程又New了一个对象A,此时A对象又引用了Old Gen中的B对象(因为并发标记阶段并不是Stop-the-world,所以程序进程和标记进程是并发执行的)。那么这个对象B就不应该被回收掉,因为被A捞了一把,手牵手进入了GC ROOT Trace。这个B在并发预处理阶段就会被标记成alive。 重标记:这个阶段也是要Stop-the-world的,重新扫描堆中的对象,再次进行可达性分析,标记alive的对象。 并发清理:重新激活用户线程,然后清理哪些dead Objects(不存在引用的对象)。 重置:CMS清楚内部状态,准备下一次回收。 为了更好地说明CMS回收的过程,这里贴一段实际场景中的GC日志:
关于CMS算法的优缺点,还有具体实现的的一些细节,这里就不做过多叙述了,有兴趣的可以自行查阅资料。 Part3.MAT分析工具和Jprofile分析工具 一顿操作猛如虎,基本了解了JVM的内存的组成和垃圾回收相关的基础信息。然后就要来分析一下WEB应用到底问题出在哪了。工欲善其事,必先利其器,然后我就下载了MAT和Jprofile。 (编辑:晋中站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |