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常见问题分析(内存溢出、内存泄露、线程阻塞等)

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

Java String 的十大常见问题

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

[译] JAVA初学者的30个常见问题

- - 博客园_首页
本文回答了30个JAVA入门级初学者的常见问题. a += b 和 a = a + b 的效果有区别吗. 声明一个数组为什么需要花费大量时间. 为什么JAVA库不用随机pivot方式的快速排序. Q. 为什么 -0/3 结果是 0,而  -0.0/3.0 结果是 -0.0. A. 在Java里,整数是用补码表示的.

HBase工程师线上工作经验总结----HBase常见问题及分析

- - 互联网 - ITeye博客
阅读本文可以带着下面问题:. 1.HBase遇到问题,可以从几方面解决问题. 2.HBase个别请求为什么很慢. 3.客户端读写请求为什么大量出错. 4.大量服务端exception,一般原因是什么. 6.Hbase数据写进去,为什么会没有了,可能的原因是什么. regionserver发生abort,遇到最多是什么情况.

linux xampp常见问题

- We_Get - 博客园-首页原创精华区
1.安装xampp4linux后,只能本机(http://localhost)访问,局域网内其他机器无法访问. 解答:在/opt/lampp/etc中修改httpd.conf,将Listen 80修改为Listen 本机ip地址:80 本机ip地址使用ifconfig 查看. 2.我按照1修改之后,局域网内的机器还是无法访问.

storm常见问题解答

- - BlogJava-庄周梦蝶
    最近有朋友给我邮件问一些storm的问题,集中解答在这里. 一、我有一个数据文件,或者我有一个系统里面有数据,怎么导入storm做计算. 你需要实现一个Spout,Spout负责将数据emit到storm系统里,交给bolts计算. 怎么实现spout可以参考官方的kestrel spout实现:.

MariaDB常见问题FAQ

- - OurMySQL
MariaDB常见问题,同样适用于MySQL. 老版本MariaDB服务的相关旧信息. via似乎是个关键字,但是至少在MySQL5.1文档中找不到. 在MySQL5.1中执行成功,但是会出现1064错误 (毫无疑问,用avia替代via就可以). 答           elenst. 这个bug(https://bugs.launchpad.net/maria/+bug/1010351)被修复.

hadoop配置常见问题

- - 企业架构 - ITeye博客
收集记录一些Hadoop配置部署过程中遇到的问题. 这种方法解决了运行中的hadoop的safe mode问题,但是下次重启hadoop,还会出现这个问题. 其实这个问题,我猜测可能是由于目录/app/hadoop/tmp/mapred/system被破坏造成. 永久解决,可以删除掉/app/hadoop/tmp/,重新创建,重新format,重启hadoop——如果条件允许的话.

Zookeeper常见问题整理

- - CSDN博客推荐文章
当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态. Zk的选举算法使用ZAB协议:. 选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server;.

[Java] Java 多线程案例分析

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