java虚拟机2-常见内存溢出及解决方法

JVM常用参数及常见内存溢出解决方法

在我们日常开发中,都有可能遇到内存溢出(java.lang.OutOfMemoryError)问题,要解决这类问题必须要了解一定的JVM内存区域划分的知识,并且要知道内存溢出具体在那块内存产生,产生的原因。这样才能够快速定位,并解决问题。

内存溢出和内存泄露

首先我们复习两个概念

  1. 内存泄露:程序在申请内存后,无法自己释放已申请的内存空间,始终占用着内存,即被分配的对象可达但无用。一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。
  2. 内存溢出::程序在申请内存时,没有足够的大内存供申请者使用。此时就会报错OutOfMemoryError,即所谓的内存溢出。

从上面可以看到,内存泄露最终将导致内存溢出。

JAVA常见内存溢出及相关解决方法

下面我们来看下常见的内存溢出。以及解决办法。
在了解内存溢出这个过程中,我们会讲到一些JVM参数。最后我们会把这些参数汇总。

堆内存溢出

堆 (HEAP) 主要是用来存放java程序中所产生的对象实例,它被程序中所有线程共有。出现堆内存溢出一般有两种情况。 一个原因是内存真的不够,另一个原因是程序中有死循环。下面我们来模拟这种情况:
首先介绍两个JVM 启动参数:
1. -Xms20M:设置JVM启动内存的最小值为20M。
2. -Xmx20M:设置JVM启动内存的最大值为20M。

将-Xms -Xmx设置为一样可以避免JVM内存自动扩展。比如这里的-Xms20M和 -Xmx20M 。

堆溢出模拟,只要一直创建对象并且不回收,那么对象数量达到最大堆容量限制后就会产生堆内存溢出。这里设置虚拟机参数 -Xms20M -Xmx20M 是为了候把堆的大小固定住并且让堆不可扩展。

/**
 * Created by IntelliJ IDEA.
 * User:hubin
 * Description:测试堆溢出 虚拟机参数 -Xms20M -Xmx20M
 * Date:2018/5/8
 * Time:14:56
 */
public class TestHeapOverflow {
    public static void main(String agrs[]){

        List<TestHeapOverflow> date = new ArrayList<>();
        while (true){
            date.add(new TestHeapOverflow());
        }
    }
}

运行结果为如下:
堆内存溢出
从控制台打印出来的信息来看。
java.lang.OutOfMemoryError: Java heap space 表示的是head Space 溢出,及是堆空间溢出。
这种内存溢出解决方法是,可以调大堆空间的大小或者从检查代码查看是否有对象生命周期过长,死循环等。

方法区(永久代)溢出

JDK1.7及之前的版本, 永久代用于存储内存中的 class 定义, 包括 class 的 名称(name), 字段(fields), 方法(methods)和字节码(method bytecode); 以及常量池(constant pool information); 对象数组(object arrays)/类型数组(type arrays)所关联的 class等.
在JDK1.7之后 溢出了永久代
方法区内存溢出是,java.lang.OutOfMemoryError: PermGen space

package OOM;
import javassist.ClassPool;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User:hubin
 * Description:
 * Date:2018/5/8
 * Time:16:23
 */
public class TestPermGenOverflow {
    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 100000000; i++) {
            generate("tstPermGenOverflow.class.Test" + i);
        }
    }

    public static Class generate(String name) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        return pool.makeClass(name).toClass();
    }
}

JDK1.7 之前是报 :
java.lang.OutOfMemoryError: PermGen space 错误信息所表达的意思是: 永久代(Permanent Generation) 内存区域已满
JDK1.7之后报
这里写图片描述

Exception in thread “main” java.lang.OutOfMemoryError: GC overhead limit exceeded 。这种情况发生的原因是, 程序基本上耗尽了所有的可用内存, GC也清理不了。

1.7 之前解决 PermGen space 的方法是:可以增加 永久代 的区域大小 -XX:MaxPermSize=256m

栈溢出

虚拟基栈是每个线程单独拥有的,栈溢出通俗的将是指如果线程方法调用的深度太深,就会产生栈溢出了。Java.lang.StackOverflowError
这里在复习一个虚拟机参数:
1. -Xss128k:指的是可以虚拟机栈的大小为128k

我们只需要写一个方法一只调用自身就可以出现StackOverflowError

package OOM;

/**
 * Created by IntelliJ IDEA.
 * User:hubin
 * Description:栈溢出测试
 * Date:2018/5/8
 * Time:16:04
 */
public class TestStackOverflow {

    public static int callNumber = 0;
    public void stackCall(){
        callNumber ++;
        stackCall();
    }
    public static void main(String agrs[])throws Throwable{
        try {
            TestStackOverflow testStackOverflow = new TestStackOverflow();
            testStackOverflow.stackCall();
        }catch (Throwable  e){
            System.out.println("callNumber :"+callNumber);
            throw e;
        }

    }
}

运行结果为:
栈内存溢出
可见 方法调用太深会出现 Java.lang.StackOverflowError
解决方法:修改程序,通常方法调用深度不要调用太深,特别是递归。在程序中最好不要递归的层数太多,因为递归有可能出现Java.lang.StackOverflowError 并且递归的效率太低。

总之 大多数的内存泄露一般都是程序没有释放内存。调整JVM 参数只能应急解决。如果想从根本上解决问题, 则需要排查内存分配相关的代码. 简单来说, 就是找到 哪类对象占用了最多内存?这些对象是在哪部分代码中分配的。

猜你喜欢

转载自blog.csdn.net/hubin916/article/details/80236114