JVM-堆分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/cdw8131197/article/details/67634523

一、内存溢出的原因分析

堆溢出:创建的对象太多或太大,占用大量的堆内存,而且未及时GC;

示例代码:

import java.util.ArrayList;

public class OOMTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list=new ArrayList<byte[]>();
        for(int i=0;i<1024;i++){
            list.add(new byte[1024*1024]);
        }
    }
}

设置了最大堆和最小堆 -Xmx5m -Xms1m

运行结果:
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
at com.cn.oneday.OOMTest.main(OOMTest.java:9)

异常信息关键词: java.lang.OutOfMemoryError: Java heap space

解决堆溢出方法:1、增大堆内存空间;2、及时做垃圾回收

永久区(方法区)溢出:当产生大量的类时,可能造成系统永久区溢出

示例代码:

public static void main(String[] args) {
    for(int i=0;i<100000;i++){
        CglibBean bean = new CglibBean("geym.jvm.ch3.perm.bean"+i,new HashMap());
    }
}

运行结果:
Caused by: java.lang.OutOfMemoryError: PermGen space
[Full GC[Tenured: 2523K->2523K(10944K), 0.0125610 secs] 2523K->2523K(15936K),
[Perm : 4095K->4095K(4096K)], 0.0125868 secs] [Times: user=0.02 sys=0.00, real=0.01 secs]
Heap
def new generation total 4992K, used 89K [0x28280000, 0x287e0000, 0x2d7d0000)
eden space 4480K, 2% used [0x28280000, 0x282966d0, 0x286e0000)
from space 512K, 0% used [0x286e0000, 0x286e0000, 0x28760000)
to space 512K, 0% used [0x28760000, 0x28760000, 0x287e0000)
tenured generation total 10944K, used 2523K [0x2d7d0000, 0x2e280000, 0x38280000)
the space 10944K, 23% used [0x2d7d0000, 0x2da46cf0, 0x2da46e00, 0x2e280000)
compacting perm gen total 4096K, used 4095K [0x38280000, 0x38680000, 0x38680000)
the space 4096K, 99% used [0x38280000, 0x3867fff0, 0x38680000, 0x38680000)
ro space 10240K, 44% used [0x38680000, 0x38af73f0, 0x38af7400, 0x39080000)
rw space 12288K, 52% used [0x39080000, 0x396cdd28, 0x396cde00, 0x39c80000)

从GClog可以看出,堆空间没有太大的使用,主要使用的时永久区,永久区内存不够,导致溢出

解决方案:1、允许垃圾回收;2、增大永久区内存

扫描二维码关注公众号,回复: 3691174 查看本文章

永久区的垃圾收集主要回收两部分内容:废弃常量和无用的类。回收废弃常量与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串“abc”已经进入了常量池中,但是当前系统没有任何一个String对象是叫做“abc”的,换句话说是没有任何String对象引用常量池中的“abc”常量,也没有其他地方引用了这个字面量,如果在这时候发生内存回收,而且必要的话,这个“abc”常量就会被系统“请”出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”:
1、该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
2、加载该类的ClassLoader已经被回收。
3、该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
虚拟机可以对满足上述3个条件的无用类进行回收,这里说的仅仅是“可以”,而不是和对象一样,不使用了就必然会回收。是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用
-verbose:class;
-XX:+TraceClassLoading;
-XX:+TraceClassUnLoading查看类的加载和卸载信息。
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
但是:在jdk1.8中移除了永久区—元空间(Metaspace)!!一个新的内存空间的诞生,代替了永久区
这就意味着java.lang.OutOfMemoryError: PermGen的空间问题将不复存在,并且不再需要调整和监控这个内存空间
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

Java栈溢出:这里的栈溢出指,在创建线程的时候,需要为线程分配栈空间,这个栈空间是向操作系统请求的,如果操作系统无法给出足够的空间,就会抛出OOM

示例代码:

public static class SleepThread implements Runnable{
    public void run(){
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String args[]){
    for(int i=0;i<1000;i++){
        new Thread(new SleepThread(),"Thread"+i).start();
        System.out.println("Thread"+i+" created");
    }
}

JVM设置:-Xmx1g -Xss1m

运行结果:Exception in thread “main” java.lang.OutOfMemoryError:
unable to create new native thread

解决方案:1、减少堆内存;2、减少线程栈大小;

**直接内存溢出:**ByteBuffer.allocateDirect()无法从操作系统获得足够的空间;
解决方法:1、减少堆内存;2、有意触发GC

二、MAT使用基础

mat下载地址:http://www.eclipse.org/mat/

设置jvm可以将内存溢出是的dump导出来,用mat打开
这里写图片描述

打开后点击“柱形图表”
这里写图片描述
通过此图可以看出每个类所占用的空间大小

这里写图片描述

支配者被回收,被支配对象也被回收

这里写图片描述
这里写图片描述

猜你喜欢

转载自blog.csdn.net/cdw8131197/article/details/67634523