GC输出日志分析

基于JDK7

verbose命令

Java -verbose:gc中参数-verbose:gc表示输出虚拟机中GC的详细情况。
使用后输出如下:[Full GC 168K->97K(1984K),0.0253873 secs]
解读如下:箭头前后的数据168K和97K分别表示垃圾收集GC前后所有存活对象使用的内存容量,说明有168K-97K=71K的对象容量被回收,括号内的数据1984K为堆内存的总容量,收集所需要的时间是0.0253873秒(这个时间在每次执行的时候会有所不同)。

JVM启动参数启用verbose GC

通过JVM启动参数设置来启用verbose gc,并指定了名字和gc日志文件存储路径。

JVM内存泄漏分析

问题

Java线程是JVM基础的一部分。你的Java堆空间内存占用不仅仅是由于静态对象和长生命的对象导致,还有可能因为短生命对象。
OutOfMemoryError问题经常被误认为是内存泄露引起。我们经常忽略错误的线程执行模型和它们持有的JVM里的短生命对象,直到它们的执行完成我们才发现。
在这种问题情形下:

  • 程序中短生命/无状态对象(XML,JSON数据负载等)被线程持有的时间会变得很长(线程锁争用,大量数据负载,远程系统的慢响应时间等)。
  • 这种短生命对象会因为垃圾收集而晋升到长生命空间,比如老年代空间。
  • 副作用是会导致老年代空间很快被占满,增加了Full GC(major收集)的频率。
  • 由于这种严重的情况,它将导致更多的GC垃圾收集,增加JVM暂停时间和最终的OutOfMemoryError:Java堆空间。

你的应用此时被停掉,你很疑惑到底怎么回事。最后,你考虑增加Java堆空间或者寻找哪里有内存泄露,你真的找对路了么?
避免在线程栈大小(虚拟机栈)和Java堆内存占用之间产生混淆是非常重要的。线程栈(虚拟机栈)大小是一种特殊的内存空间,它被JVM用于存储每个方法调用。当一个线程调用方法A,它将这个调用入栈。如果方法A调用方法B,同样也会入栈。一旦方法执行完毕,这个调用便从栈里出栈。
这种线程方法调用会导致Java对象产生,并分配在Java堆里。增加线程栈的大小是没有任何效果的(对象最终在堆得年轻代(Eden)产生)。而调整线程栈大小通常是要处理java.lang.stackoverflowerror错误或者OutOfMemoryError: unable to create new native thread错误的时候才会需要。

JVM调优案例分析-Eclipse启动

为什么要进行JVM调优

运行在JVM上的程序都会进行内存分配以及垃圾回收,在这个过程中设置合理的内存大小及垃圾回收算法能显著提高应用的响应速度及运行效率,相反不合理的JVM参数设置会造成应用程序响应不稳定并导致整个应用程序挂掉。

如何JVM调优

不同类型应用程序的JVM参数设置都不一样,如何设置最优的JVM参数不仅需要对GC机制有一定的了解,而且还要反复的试验才能得出最合适的JVM参数值。
例如:某些系统要求运行稳定、并且响应速度高,这类应用就需要通过调整其JVM参数来减少内存大小调整及垃圾回收所占用的时间,以尽量的提高响应速度。报表类及容易产生大对象对响应速度要求不是很高的系统,可以把堆空间设置较大些。
除了了解GC的机制外等一些基本调优方法外,往往还需要借助一些监控工具来进行JVM参数调优,例:Jprofiler 、VisualVM、Jconsole等,通过这些工具可以监控JVM运行时内存分配情况,线程状态、数量,堆空间类、对象数量类型信息等,此外还可以借助分析垃圾回收日志对JVM进行优化。

JVM垃圾回收算法-CMS(Concurrent Low Pause Collector)

介绍

CMS,全称Concurrent Low Pause Collector,是jdk1.4后期版本开始引入的新gc算法,在jdk5和jdk6中得到了进一步改进,它的主要适合场景是对响应时间的重要性需求 大于对吞吐量的要求,能够承受垃圾回收线程和应用线程共享处理器资源,并且应用中存在比较多的长生命周期的对象的应用。CMS是用于对Tenured Generation的回收,也就是年老代的回收,目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代。
在我们的应用中,因为有缓存的存在,并且对于响应时间也有比较高的要求,因此希望能尝试使用CMS来替代默认的server型JVM使用的并行收集器,以便获得更短的垃圾回收的暂停时间,提高程序的响应性。
CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,它的收集周期是这样。

  1. 初始标记(CMS-initial-mark)
  2. 并发标记(CMS-concurrent-mark)
  3. 重新标记(CMS-remark)
  4. 并发清除(CMS-concurrent-sweep)
  5. 并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。

其中的1,3两个步骤需要暂停所有的应用程序线程的。第一次暂停从root对象开始标记存活的对象,这个阶段称为初始标记;第二次暂停是在并发标记之后, 暂停所有应用程序线程,重新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致)。第一次暂停会比较短,第二次暂停通常会比较长,并且remark这个阶段可以并行标记。
而并发标记、并发清除、并发重设阶段的所谓并发,是指一个或者多个垃圾回收线程和应用程序线程并发地运行,垃圾回收线程不会暂停应用程序的执行,如果你有多于一个处理器,那么并发收集线程将与应用线程在不同的处理器上运行,显然,这样的开销就是会降低应用的吞吐量。remark阶段的并行,是指暂停了所有应用程序后,启动一定数目的垃圾回收进程进行并行标记,此时的应用线程是暂停的。
CMS的Young Generation的回收采用的仍然是并行复制收集器,这个跟Paralle GC算法是一致的。

JVM内存调整(五)

JVM运行时数据区的内存大小可以通过参数来设置,通常能设置的两块区域为堆空间和持久代(方法区),设置方法是以参数的形式来指定,Sun的HotSpot需要在JVM启动前设置这些参数,启动JVM后不能动态改变其大小。
JVM参数说明:

JVM垃圾回收(四)

垃圾回收概念

什么是垃圾回收

JVM中自动检测并移除不再使用的数据对象的这种机制称为:垃圾回收(Garbage Collection),简称GC。

GC的基本原理

JVM通过使用垃圾收集器及使用相应的垃圾回收算法将内存中不再被使用的对象进行回收。

为什么要垃圾回收

由于不同Java对象存活时间是不一定的,因此,在程序运行一段时间以后,如果不进行垃圾回收,整个程序会因内存耗尽导致整个程序崩溃。垃圾回收还会整理那些零散的内存碎片,碎片过多最直接的问题就是会导致无法分配大块的内存空间以及降低程序的运行效率。

哪些区域会被GC

VM栈、本地方法栈以及程序计数器会随方法或线程的结束而自然被回收,所以这些区域不需要考虑回收问题。堆空间和持久代(方法区)是GC回收的重点区域,不同区域对象的收集叫法不一样。

  • 对年轻代的对象的收集称为minor GC(eden:minor gc,整个新生代:major gc)。
  • 对老年代的对象的收集称为Full GC。程序中主动调用System.gc()强制执行的GC为Full GC。Full GC基本都是整个堆空间及持久代发生了垃圾回收,通常优化的目标之一是尽量减少GC和Full GC的频率。
  • 持久代的垃圾回收和老年代的垃圾回收是绑定的,一旦其中一个区域被占满,这两个区都要进行垃圾回收(Full GC)。

JVM运行数据区(三)

Runtime Data Area主要包括五个部分:

  • Heap(堆)
  • Method Area(方法区域)
  • VM Stack(虚拟机栈)
  • Native Method Stack(本地方法栈)( 在Sun的HotSpot虚拟机中VM Stack和Native method stack是合并到一起的)
  • Program Counter(程序计数器)

Heap和Method Area是被所有线程的共享使用的,而Vm Stack, Program Counter和Native Method Stack是以线程为粒度的,每个线程独自拥有。

JVM类加载器(二)

类加载器负责加载Java类的字节代码到Java虚拟机中,可以根据指定的类名(如java.lang.Object)来装载class文件的内容到Runtime Data Area中的Method Area(方法区域)。Java程序员可以extends java.lang.ClassLoader类来写自己的Class loader

类加载器的模型

类加载器双亲委派模型

如果一个类加载器接收到了类加载的请求,它首先把这个请求委托给他的父类加载器去完成,每个层次的类加载器都是如此,因此所有的加载请求都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它在搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

双亲委派模型的优点

Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如类java.lang.Object,它存放在tools.jar中,无论哪个类加载器要加载这个类,最终都会委派给启动类加载器进行加载,因此Object类在程序的各种类加载器环境中都是同一个类。相反,如果用户自己写了一个名为java.lang.Object的类,并放在程序的Classpath中,那系统中将会出现多个不同的Object类,Java类型体系中最基础的行为也无法保证,应用程序也会变得一片混乱。

JVM体系结构(一)

本系列基于JDK7

JVM体系结构

主要包含两个子系统和两个组件。

  • Class Loader(类加载器)子系统, Execution Engine(执行引擎)子系统。
  • Runtim Data Area(运行时数据区域)组件,Native Interface(本地接口)组件。

    JVM体系结构图

JVM堆内存,堆外内存

概念

JVM可以使用的内存分外2种:堆内存和堆外内存。

堆内存

由JVM负责分配和释放,如果程序没有缺陷代码导致内存泄露,那么就不会遇到java.lang.OutOfMemoryError(OOM)这个错误。

堆外内存

直接分配和释放内存,提高效率。堆外内存意味着把内存对象分配在Java虚拟机的堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机)。这样做的结果就是能保持一个较小的堆,以减少垃圾收集对应用的影响。JDK5.0之后,代码中能直接操作本地内存的方式有2种:使用未公开的UnsafeNIOByteBuffer。存储对象生命周期比较长,对象数据结构比较单一。

堆外内存优点

  • 可以扩展至更大的内存空间。
  • 理论上能减少GC暂停时间。因为垃圾回收会暂停其他的工作。
  • 可以在进程间共享,减少JVM间的对象复制,使得JVM的分割部署更容易实现。因为堆内在flush到远程时,会先复制到直接内存(非堆内存),然后在发送;而堆外内存相当于省略掉了这个工作。
  • 它的持久化存储可以支持快速重启,同时还能够在测试环境中重现生产数据。

    堆外内存缺点

  • 堆外内存难以控制,如果内存泄漏,那么很难排查。
  • 数据结构不能复杂化,结构单一,最好是基础类型。使用序列化和反序列化,性能比堆内对象还差。
  • 使用堆外内存,不用考虑JVM内存限制,但需要考虑磁盘系统。(HDD或者SSD)
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×