从头开始学JVM--------内存管理

一、基本内存分布

         堆内存:堆内存是所有线程共享的一块内存区域,由虚拟机在启动的时候创建。唯一目的就是存放对象实例,几乎所有对象实例都在堆里分配内存。数组也在这里分配内存。这里也是垃圾回收器工作的主要区域

         栈内存:栈内存存储基本数据类型,方法引用以及方法里面的临时变量

         方法区:用于存储已被虚拟机加载的类信息、静态变量等数据,还有就是常量池,比如字符串常量池。

                       类一般都会有其 接口信息,父类信息,方法信息。在Java堆中必须能查找到此对象的这些信息。

         本地方法栈:本地方法栈就是一块为native方法服务的内存区域。

         程序计数器:程序计数器是用于记录程序执行到哪的一个指针。因为多个线程之间来回切换,需要记录上次程序执行到哪里。

二、堆内存溢出OutOfMemoryError

        配置JVM参数, -Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError

-Xms5m  设置堆内存最小值5m

-Xmx5m  设置堆内存最大值5m   最小值与最大值设置一样,可以避免堆内存自动扩展

-XX:+HeapDumpOnOutOfMemoryError   记录当出现堆内存溢出时的信息

        使用一个死循环,不断创建对象,将其放入List集合中。 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

public class TestBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(TestBean.class);

    static class OOMObject {
    }

    /**
     * VM Args : -Xms5m -Xmx5m -XX:+HeapDumpOnOutOfMemoryError
     */
    public static void main(String[] args) {
        int count = 0;
        List<OOMObject> list = new ArrayList<>();

        while (true) {
            LOGGER.info("count={}", count++);
            list.add(new OOMObject());
        }
    }
}

         我本机执行结果,当执行到创建第160065个对象的时候,加入List集合,集合最后一次扩容。在ArrayList扩容过程中,出现了OOM。ArrayList实际上就是一个数组,下图中红色部分的堆栈信息应该很熟悉,就是数组在扩容的代码。ArrayList源码参考链接

        总结:

        Java堆内存的OOM异常是实际中最常见的内存溢出情况。

        异常提示信息为: java.lang.OutOfMemoryError :Java heap space。

        解决思路为:找出是 内存泄露 还是 内存溢出

 三、栈内存溢出StackOverflowError

         配置JVM参数, -Xss128k

-Xss128k  配置栈内存为128k

        

         跑下面这段代码。

public class TestOsfBean {

    private int count;

    public void stackOverflowMethod() {
        this.count++;
        //递归调用,直到栈内存溢出
        this.stackOverflowMethod();
    }

    /**
     * VM Args: -Xss128k
     */
    public static void main(String[] args) {
        TestOsfBean osfBean = new TestOsfBean();
        try {
            osfBean.stackOverflowMethod();
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("栈内存溢出,count = " + osfBean.count);
        }
    }
}

        运行结果:递归调用了993次后,栈内存顶不住了,抛出 java.lang.StackOverflowError。

总结:

        栈内存溢出的异常提示信息为: java.lang.StackOverflowError 

        解决思路为:排查递归方法,如果某个递归方法确实需要超过上千次,应该考虑提高栈内存大小。配置示例 -Xss1024k

四、方法区内存溢出

         本来想测试方法区内存溢出的,使用字符串String的intern()方法来测试。但是怎么测试好像都没有效果。

         错误信息总是提示 java.lang.OutOfMemoryError : Java heap space 。 这显然是堆内存溢出的情况。

         错误日志里面显示 :翻阅相关资料,说JDK1.8之后,permgen空间不再存在,现在是普通堆的一部分。我服了。

         最直接的后果,就是以后再也不会有outofmemoryerror permgen space这个错误了。

         jdk8移除了永久代PermGen,取而代之的是元空间MetaSpace。

 替换理由:       

        permSize:原来的jar包及你自己项目的class存放的内存空间,这部分空间是固定的,启动参数里面-permSize确定,如果你的jar包很多,经常会遇到permSize溢出,且每个项目都会占用自己的permGen空间改成metaSpaces,各个项目会共享同样的class内存空间,比如两个项目都用了fast-json开源包,在metaSpaces里面只存一份class,提高内存利用率,且更利于垃圾回收。

        原来如此啊!

posted @ 2022-07-17 12:14  小大宇  阅读(27)  评论(0编辑  收藏  举报