Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

标签: java 问题 分析 | 发表时间:2017-06-13 10:42 | 作者:lixuguang
分享到:
出处:http://www.iteye.com
  1. Java垃圾回收机制(GC) 
    1.1  GC机制作用 
    1.2  堆内存3代分布(年轻代、老年代、持久代) 
    1.3  GC分类 
    1.4  GC过程
  2. Java应用内存问题分析 
    2.1  Java内存划分 
    2.2  Java常见内存问题 
    2.3  ML(内存泄露) OOM(内存溢出)问题现象及分析 
    2.4  IBM DUMP分析工具使用介绍
  3. Java应用CPU、线程问题分析

Java垃圾回收机制(GC)

1.GC机制作用 
1.1 JVM自动检测和释放不再使用的对象内存 
1.2 Java 运行时JVM会执行 GC,不再需要显式释放对象 
例:Object.finallize()、 Windows.dispose()、 System.gc()

这里写图片描述

2.Java堆3代分布

这里写图片描述

关于Java堆3代分布情况,可通过命令:jmap –heap pid 查看

这里写图片描述

3.GC分类 
3.1 Young GC(Minor GC):收集生命周期短的区域(Young) 
(1) 清空Eden+from survivor中所有no ref的对象占用的内存 
(2) 将Eden+from survivor中所有存活的对象copy到to survivor中 
(3) 一些对象将晋升到old中: to survivor放不下的或存活次数超过turning threshold中的 
3.2 Full GC(Major GC):收集生命周期短的区域(Young)和生命周期比较长的区域(Old),对整个堆进行垃圾收集,有时也会回收持久区(Perm) 
(1) 清空heap中no ref的对象 
(2) 清空permgen中已经被卸载的class信息

4.GC过程 
(1) 新生成的对象在Eden区完成内存分配 
(2) 当Eden区满,再创建对象,会因为申请不到空间触发YGC,进行young(eden+1survivor)区的垃圾回收(为什么是eden+1survivor:两个survivor中始终有一个survivor是空的,空的那个被标记成To Survivor) 
(3) YGC时,Eden不能被回收的对象被放入到空的survivor(也就是放到To Survivor,此时Eden被清空),另一个survivor(From Survivor)里不能被GC回收的对象也会被放入To Survivor,始终保证一个survivor是空的(YGC完成之后,To Survivor 和 From Survivor的标记互换) 
(4) YGC结束后,若存放对象的survivor满,则这些对象被copy到old区,或者survivor区没有满,但是有些对象已经足够Old(超过XX:MaxTenuringThreshold),也被放入Old区 
(5) 当Old区被放满的之后,进行完整的垃圾回收,即 FGC 
(6) FGC后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现OOM错误

这里写图片描述

Java应用内存问题分析方法

1.Java内存划分 
可粗略划分三类: 
1.1 堆内存 
存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理 
这里写图片描述

1.2 栈内存 
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配(更准确地说是保存了引用的堆内存空间的地址,java中的“指针”)

1.3 永久保存区、方法区(Permanent Generation) 
用于存储已被虚拟机加载的类信息、常量、静态变量等

这里写图片描述

2.Java常见的内存问题表现形式: 
2.1 OutOfMemory:内存溢出 
2.2 Memory Leak:内存泄露 
二者共同点: 
(1) 通常最终的状态就会导致OOM错误 
(2) 在Java堆或本地内存中都可能发生 
二者不同点: 
(1) ML是已经分配好的内存或对象,当不再需要,没有得到释放 而OOM则是没有足够的空间来供jvm分配新的内存块 
(2) ML的内存曲线总体上是一条斜向上的曲线而OOM不是,反之未必

3.内存溢出类型: 
虚拟机栈溢出、本地方法栈溢出、方法区溢出、堆溢出、运行时常量池溢出 
异常类型: 
(1) java.lang.OutOfMemoryError:  Java heap space 
堆内存溢出 
优化:通过-Xmn(最小值)–Xms(初始值) -Xmx(最大值)参数手动设置 Heap(堆)的大小。

(2) java.lang.OutOfMemoryError: PermGen space 
PermGen Space溢出(方法区溢出、运行时常量池溢出) 
优化:通过MaxPermSize参数设置PermGen space大小。

(3) java.lang.StackOverflowError 
栈溢出(虚拟机栈溢出、本地方法栈溢出) 
优化:通过Xss参数调整

Demo代码 :

	// Java 堆溢出
    public static void main(String[] args) {
        List<OOMObject> list = new    ArrayList<JavaHeapSpace.OOMObject>();
        while (true) {
            list.add(new OOMObject());
        }
    }

    static class OOMObject {

    }

   // 虚拟机栈溢出
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(add());
    }

    public static int add(){
        return add();
    }      

	// 方法区溢出
    public static void main(String[] args) {
        while (true) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(OOMObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object obj, Method method,
                        Object[] args, MethodProxy proxy) throws Throwable {
                    return proxy.invoke(obj, args);
                }
            });
            enhancer.create();
        }
    }

    static class OOMObject {

    }

	// 运行时常量池溢出
    public static void main(String[] args){
        // TODO Auto-generated method stub
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true ){
             list.add(String. valueOf(i++).intern());
       }
    }

	// 内存泄露模拟
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<int[]> list = new ArrayList<int[]>();

        Runtime run = Runtime.getRuntime();

        int i=1;

        while(true){
            int[] arr = new int[1024];
            list.add(arr);

            if(i++ % 1000 == 0 ){
                System.out.print("最大堆内存=" + run.maxMemory() / 1024 / 1024 + "M, ");
                System.out.print("已分配内存=" + run.totalMemory() /1024 / 1024 + "M, ");
                System.out.print("剩余空间内存=" + run.freeMemory() / 1024 / 1024 + "M, ");
                System.out.println("最大可用内存=" + ( run.maxMemory() - run.totalMemory() + run.freeMemory() ) / 1024 / 1024 + "M");
                sleep(1000);
            }
        }
    }

    public static void sleep(long time) {
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

 4.内存泄露现象 

这里写图片描述

heapspace:OutOfMemoryError 
这里写图片描述

开发人员的分析、解决思路 
内存对象申请未释放(未及时释放) 
线程问题 
分别从堆dump和线程dump进行分析: 
jmap -dump:format=b,file=heap.dump pid 
jstack pid >> thread.dump

5. java DUMP分析工具 
IBM HeapAnalyzer:ha456.jar 
IBM Thread and Monitor Dump Analyzer:jca457.jar 
堆dump分析 
占用内存较多代码块 
分析代码快上下文 
分析占用内存的对象内容 
这里写图片描述

线程dump分析 
活跃线程 
阻塞线程 
等待资源线程 
这里写图片描述

Java应用CPU问题分析方法

1.程序响应慢,CPU高 
(1) ThreadDump 
jstack pid >> thread.dump 
(2) 找到导致cpu高的线程 top -H -p pid 
(3) pid 十进制转十六进制 
http://tool.oschina.net/hexconvert/ 
(4) 找到对应的线程UE打开 threaddump文件查找:按十六进制关键字找到对应的线程,把相关的方法找出来,可以精确到代码的行号

2.程序响应慢,CPU不高 
一般表现为thread struck在了i/o、db等 
实例:

IO阻塞(程序表现为响应慢)

线程状态为“in Object.wait()”,说明正在等待线程池可用资源,由于线程池满导致新的IO请求处于排队等待状态,且发生在:at com.iflytek.diange.data.provider.sendsong.impl.SendSongImpl.getSendSongInfosByUserId(SendSongImpl.java:92)行 
这里写图片描述

3.程序无响应 
死锁(程序表现为无响应) 
线程状态为“waiting to lock”: 两个线程各持有一个锁,又在等待另一个锁,故造成死锁,且发生在DeadLockTest.java:39行 
这里写图片描述



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 问题 分析] 推荐:

深入分析 Java 中的中文编码问题

- Alex - IBM developerWorks 中国 : 文档库
编码问题一直困扰着开发人员,尤其在 Java 中更加明显,因为 Java 是跨平台语言,不同平台之间编码之间的切换较多. 本文将向你详细介绍 Java 中编码问题出现的根本原因,你将了解到:Java 中经常遇到的几种编码格式的区别;Java 中经常需要编码的场景;出现中文问题的原因分析;在开发 Java web 程序时可能会存在编码的几个地方,一个 HTTP 请求怎么控制编码格式.

java程序cpu占用过高问题分析

- - Web前端 - ITeye博客
针对某个java程序cpu占用过高问题分析,要想找到问题的真正原因,首先要明确cpu过高的进程,通过对进程下线程的分析,定位到具体的应用代码,从而定位问题的原因所在.     在jdk自带的分析工具中,通过jconsole只能分析到应用程序的相关系统资源使用情况,但无法定位应用程序,故通过此工具了解到应用程序存在问题,但要具体定位到哪块程序不合理造成的是很困难的.

Java常见问题分析(内存溢出、内存泄露、线程阻塞等)

- - Java - 编程语言 - ITeye博客
Java垃圾回收机制(GC) . 堆内存3代分布(年轻代、老年代、持久代) . ML(内存泄露) OOM(内存溢出)问题现象及分析 . IBM DUMP分析工具使用介绍. Java应用CPU、线程问题分析. Java垃圾回收机制(GC). 1.GC机制作用 . 1.1 JVM自动检测和释放不再使用的对象内存 .

[Java] Java 多线程案例分析

- - V2EX
现要从 hbase中导出 2016 年整年的,大约 10w只股票行情数据,数据总量约 100t. 汇总到 hdfs中供需求方使用. 已知数据量规模大概是 100t,那么单台机器处理肯定不是不行的,先不说大多数磁盘都没这么大,即便磁盘有这么大,单台机器处理对于内存和 cpu 要求也很高,所以我们将问题一般化,使用数量有限的低配机器.

java线程池分析

- - BlogJava-首页技术区
    在Java 5.0之前启动一个任务是通过调用Thread类的start()方法来实现的,任务的提于交和执行是同时进行的,如果你想对任务的执行进行调度或是控制 同时执行的线程数量就需要额外编写代码来完成. 5.0里提供了一个新的任务执行架构使你可以轻松地调度和控制任务的执行,并且可以建立一个类似数据库连接 池的线程池来执行任务.

JAVA集合分析概述

- - ITeye博客
       前段时间在QQ空间写了篇关于JAVA集合分析的日志,现搬到这里与大家分享. 其中有链接可查看各种集合的具体介绍.  最基本的数据结构有数组、链表2种.         1)数组的长度是固定的,容量是有限的(除非扩容). 可以用索引访问,因此访问速度比较快.         2)链表中每个节点指向下一个节点,容量是无限的(不作限制且不考虑硬件).

Java ClassLoader原理分析

- - Java - 编程语言 - ITeye博客
一、JDK默认提供的三个ClassLoader. JDK 默认提供了如下几种ClassLoader. Bootstrp加载器是用C++语言写的,它是在Java虚拟机启动后初始化的,它主要负责加载 %JAVA_HOME%/jre/lib, -Xbootclasspath参数指定的路径以及 %JAVA_HOME%/jre/classes中的类.

Java String 的十大常见问题

- - ITeye博客
Java字符串经常被问到的排名前十的问题.    1、如何比较字符串. 使用 “==”  还是 “equals()”.   简单来讲,“==”比较的是引用(对象的内存地址),“equals()” 比较值是否相等. 除非你想检测两个字符串是否是同一对象,否则都用equals().   当然了解字符串池的概念更好.

Java写xml文件的编码问题

- - CSDN博客推荐文章
最近项目中需要生成xml格式的配置文件,用的是 javax.xml.transform.Transformer 类中提供的transform方法,在本地执行没问题,但是一旦把工程部署到Tomcat下运行,就会出现中文乱码的现象,纠结了许久,在大神的帮助下终于解决了. 有篇文章其实已经讲的很清楚了,链接如下:.

java socket 长连接read阻塞问题

- - PHP & Java
1 约定发送的数据长度,比如 http的 keepAlive 就是必须依赖这个的 Content-Length. 2 设置超时的时间,根据我的经验,只有在Socket级别设置才有效. socket.setSoTimeout(100); // 如果超过100毫秒还没有数据,则抛出 SocketTimeoutException.