• Memory Analyzer Tool定位Java heap space内存泄漏


    java heap space是一个很蛋疼的问题,如果开发调试时遇到还好,如果是在项目上线后运行一段时间后,才抛出该异常,那真的很悲剧(那你得找代码中到底是哪里内存泄露了),这真是一个悲伤的故事。  

    1.java head space堆内存溢出

    分  析

    发生这种问题的原因是java虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与Heap space有关。

    解决方法:

    1. 增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m。

    2. 检查程序,看是否释放发生内存泄露(对象未释放、集合过大、网络连接未关闭、算法有问题...),找到泄露的地方,修改程序。

    第一种方法适用场景:

    jvm的最大内存设置过小(一般的项目设置成1G就足够了,还发生这种异常就有点不正常了)。

    第二种方法适用的场景:

    对象内存不能被java虚拟机释放,并且不断增长,占用了很大的空间,导致jvm内存不足。

    2.利用MAT工具定位内存泄露

     

    了解jvm垃圾回收机制

    java不像C一样需要手动管理程序内存,它有自己的垃圾回收机制:每隔一段时间运行异步线程去回收垃圾来释放内存,并不需要我们辅助回收内存,但这并不代表程序员就可以随心所欲的写代码,而不用担心内存的回收了。因为java垃圾回收是有条件的,下面是垃圾回收机制的简单介绍:

    1.java虚拟机每隔一段时间调用一次垃圾回收,回收没有被引用了的内存数据,释放该内存。

    2.在java内存不足的,会频繁调用垃圾回收,释放内存,当堆中内存不足2%时,会抛出java head space异常。

    3.java垃圾回收可显示调用,方法为"System.gc()"。

    定位内存泄露

     内存泄露:对象内存不能被java虚拟机释放,并且不断增长,占用了很大的空间,导致jvm内存不足,抛出java head space。

      MAT的使用

     

    1.内存泄露代码例子

    [java] view plain copy
     
    1. package test;  
    2.   
    3. import java.util.HashMap;  
    4.   
    5. public class OOMTest{  
    6.     HashMap<Integer,String> map = new HashMap<Integer,String>();  
    7.       
    8.     public static void main(String[] args) throws InterruptedException {  
    9.         new OOMTest().addMap();  
    10.     }  
    11.       
    12.     public void addMap() throws InterruptedException{  
    13.         int i  = 0;  
    14.         while(true){  
    15.             Thread.sleep(50);  
    16.             i++;  
    17.             map.put(i, String.valueOf(i));  
    18.         }  
    19.     }  
    20. }  

        2.导出dump文件快照

     运行程序,在程序关闭前(web工程可以在抛出异常后)导出dump文件。
    导出文件命令:
    1.在cmd中执行jps命令(web工程是选择发布的tomcat,tomcat在jps的名称为“Bootstrap”)
    2.执行导出命令:
    jmap -dump:format=b,file=导出文件路径 <进程号> 

    3.用MAT分析文件

    按装eclipse mat插件,在eclipse  File —> Open File 中选择dump文件,加载dump文件。
    点击dominator_tree,展开占用内存(Retained heap)最大的,可以看到指向的类是test.OOMTest

    MAT分析小技巧

    1.将JVM内存设大,使MAT显示内存泄露处的内存占用率更加明显,排除其他因素的干扰。
    2.在实际使用当中,可以获取多个时刻的内存快照,进行前后比较,如果某一对象前后一直在增长,说明该处存在内存泄露的可能。

    3.其他常见的OOM及解决思路:

    1、OOM for Perm=>例如:java.lang.OutOfMemoryError: Java perm space

    分  析

    发生这种问题的原意是程序中使用了大量的jar或class,使java虚拟机装载类的空间不够,与Permanent Generation space有关。

    解决方法

    1.增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小,其中XX:PermSize是初始永久保存区域大小,XX:MaxPermSize是最大永久保存区域大小。

    2.如果是JVM运行较长一段时间而不是刚启动后溢出的话,很有可能是由于运行时有类被动态加载进来,此时建议用CMS策略中的类卸载配置。

    如:-XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled

    2、OOM for GC=>例如:java.lang.OutOfMemoryError: GC overhead limit exceeded

    分  析

    此OOM是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略,在一定比例下开始GC而不要使用默认的策略,或者将新代和老代设置合适的大小,需要进行微调存活率。

    解决方法

    改变GC策略,在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。

    3、OOM for native thread created=>例如:java.lang.OutOfMemoryError: unable to create new native thread

    分  析

    参考如下:

    (MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads

    MaxProcessMemory 指的是一个进程的最大内存

    JVMMemory JVM内存

    ReservedOsMemory 保留的操作系统内存

    ThreadStackSize 线程栈的大小

    如果JVM内存调的过大或者可利用率小于20%,可以建议将heap及perm的最大值下调,并将线程栈调小,即-Xss调小,如:-Xss128k

    解决方法

    在JVM内存不能调小的前提下,将-Xss设置较小,如:-Xss:128k

    4、OOM for allocate huge array=>例如:Exception in thread "main": java.lang.OutOfMemoryError: Requested array size exceeds VM limit

    分  析

    此类信息表明应用程序(或者被应用程序调用的APIs)试图分配一个大于堆大小的数组。例如,如果应用程序new一个数组对象,大小为512M,但是最大堆大小为256M,因此OutOfMemoryError会抛出,因为数组的大小超过虚拟机的限制。

    解决方法

    (1)、首先检查heap的-Xmx是不是设置的过小

    (2)、如果heap的-Xmx已经足够大,那么请检查应用程序是不是存在bug,例如:应用程序可能在计算数组的大小时,存在算法错误,导致数组的size很大,从而导致巨大的数组被分配。

    5、 OOM for small swap=>例如:Exception in thread "main": java.lang.OutOfMemoryError: request <size> bytes for <reason>. Out of swap space

    分  析

    抛出这类错误,是由于从native堆中分配内存失败,并且堆内存可能接近耗尽。这类错误可能跟应用程序没有关系,例如下面两种原因也会导致错误的发生:

    (1)操作系统配置了较小的交换区

    (2)系统的另外一个进程正在消耗所有的内存

    解决方法

    (1)、检查os的swap是不是没有设置或者设置的过小

    (2)、检查是否有其他进程在消耗大量的内存,从而导致当前的JVM内存不够分配。

    注意:虽然有时<reason>部分显示导致OOM的原因,但大多数情况下,<reason>显示的是提示分配失败的源模块的名称,所以有必要查看日志文件,如crash时的hs文件。

  • 相关阅读:
    guzzle 中间件原理
    K8S-K8S 环境搭建
    K8S-k8s 理念知识
    云计算的概念
    Linux-DHCP 交互的过程
    linux-怎么踢出系统当前已连接的用户
    linux-Centos 搭建http yum源
    linux-硬链接与软连接
    linux-centos网络配置bond
    linux-dd 一个测试文件
  • 原文地址:https://www.cnblogs.com/hanxiaohui/p/8358797.html
Copyright © 2020-2023  润新知