zoukankan      html  css  js  c++  java
  • 实战项目中Java heap space错误的解决

    部标GPS通讯系统在上线之后,经过不断调试,终于稳定运行一段时间,后来又遇到了Java heap space错误异常!日志如下:

     

    说明系统中有未释放的对象。如何找出这些未释放对象以及监控JVM堆内存,优化代码释放内存对象呢?还有JVM的垃圾回收机制是如何运作的呢?

    首先在系统启动运行的时候打开记录GC详细信息,运行脚本如下:

     看看GC详细日志,当GC到13400多次的时候新生代和老生代的堆内存几乎用满了,频繁触发Full GC (Ergonomics) ,直到没有内存空间给新生对象了。所以JVM抛出了内存溢出错误!进而导致程序崩溃。

    首先简单了解下JVM垃圾回收机制:

    从上图可以看出 GC 的主要收集区域,包括 PSYoungGen(年轻代)、ParOldGen(老年代)、Metaspace(元数据区)。

    新生代:新创建的对象都是用新生代分配内存,Eden空间不足时,触发Minor GC,这时会把存活的对象转移进幸存者Survivor区。
    老年代:老年代用于存放经过多次Minor GC之后依然存活的对象。

    新生代的GC(Minor GC):新生代通常存活时间较短基于Copying算法进行回收,所谓Copying算法就是扫描出存活的对象,并复制到一块新的完全未使用的空间中,对应于新生代,就是在Eden和FromSpace或ToSpace之间copy。

    新生代采用空闲指针的方式来控制GC触发,指针保持最后一个分配的对象在新生代区间的位置,当有新的对象要分配内存时,用于检查空间是否足够,不够就触发GC。当连续分配对象时,对象会逐渐从Eden到Survivor,最后到老年代。

    老年代的GC(Major GC/Full GC):老年代与新生代不同,老年代对象存活的时间比较长、比较稳定,因此采用标记(Mark)算法来进行回收,所谓标记就是扫描出存活的对象,然后再进行回收未被标记的对象,

    回收后对用空出的空间要么进行合并、要么标记出来便于下次进行分配,总之目的就是要减少内存碎片带来的效率损耗。

    附上堆结构图:

    至此,在已经尝试了将Java虚拟机中Xms(初始堆大小-2G)和Xmx(最大堆大小-4G)参数调大之后也无果,只能从代码入手, 对象一直堆积于老年代未被释放,应该是代码中对象未被释放。

    为了找出代码中未被释放的对象,这里运用到了内存监控工具jconsole和内存堆对象统计工具jmap

    jconsole.exe位于bin目录下,用法也挺简单的,用于调优后的监控还不错,这里留个图不做介绍了

    这里运用jmap(jdk自带的jvm内存分析的工具),将程序堆对象统计出来的结果如下:

    发现到代码问题:应该是解析协议的时候CanDataItem对象不断被new出来,使用完了以后并未被GC回收留在老生代,

     找到CanDataItem使用完成的地方做了如下修改:

    修改代码再将T808Message等类用完了的地方做了释放,之后终于解决了问题!修改代码以后再次使用jmap统计如下,正常了!

    附 - jmap输出中class name非自定义类的说明:

    BaseType CharacterTypeInterpretation
    B byte signed byte
    C char Unicode character
    D double double-precision floating-point value
    F float single-precision floating-point value
    I int integer
    J long long integer
    L; reference an instance of class
    S short signed short
    Z boolean true or false
    [ reference one array dimension,[I表示int[]

    总结导致java.lang.OutOfMemoryError异常的常见原因有以下几种:

    • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
    • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
    • 代码中存在死循环或循环产生过多重复的对象实体-----(本例中);
    • 使用的第三方软件中的BUG;
    • 启动参数内存值设定的过小;

    至此问题总算解决了,通常情况下,Java开发人员并不需要去关心JVM是如何运行的。即使不理解JVM的工作原理,也不会给开发人员带来过多困惑。Java程序员更容易忽视基础技术。

    “蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇蟮之穴无可寄托者,用心躁也”。对于技术人员来说,如果长期忽略自身技术的根基而去一昧地追求高层框架技术,这无疑是舍本求末的做法。

    JVM的出现,为程序员屏蔽了操作系统与硬件的细节,使得程序员从诸如内存管理这样的繁琐任务中解放出来。但这不并等同于允许Java程序员放弃对基础的重视。

    事实上,任何平台的程序员都应当了解平台的基本特性、实现机制以及接口,这是提高自身修养的必经之路。对于Java程序员来说,我们还是需要多多了解JVM。

    了解JVM的基本实现机制,不仅对于解决实际应用中诸如GC等虚拟机问题时有直接帮助,还有利于我们更好地理解语言本身。

    转载请注明出处!谢谢!https://www.cnblogs.com/lys_013/p/9605352.html

  • 相关阅读:
    [saiku] 系统登录成功后查询Cubes
    216. Combination Sum III
    215. Kth Largest Element in an Array
    214. Shortest Palindrome
    213. House Robber II
    212. Word Search II
    211. Add and Search Word
    210. Course Schedule II
    分硬币问题
    开始学习Python
  • 原文地址:https://www.cnblogs.com/lys_013/p/9605352.html
Copyright © 2011-2022 走看看