6.认识JVM-JVM内存模型

目录

1.认识JVM之Classfile:https://www.cnblogs.com/nuti/p/16270652.html

2.认识JVM之类加载机制:https://www.cnblogs.com/nuti/p/16270672.html

3.认识JVM之运行时数据区: https://www.cnblogs.com/nuti/p/16270703.html

4.认识JVM之编译器: https://www.cnblogs.com/nuti/p/16270741.html

5.认识JVM之垃圾收集器: https://www.cnblogs.com/nuti/p/16270755.html

6.认识JVM之JVM内存模型: https://www.cnblogs.com/nuti/p/16270816.html

 

书接上文,https://www.cnblogs.com/nuti/p/16270755.html

看到这里大概对JVM是什么样子,过程有了了解,那么这里就深入理解一下JVM内存模型吧

0.整体描述

上一篇对运行时数据区整体都描述了一番,其实重点的数据储存还是在堆和方法区(非堆),所以内存的设计也注重从这两方面展开,对于虚拟机栈 本地方法栈 程序计数器这些都是线程私有的
0
一块是非堆区,一块是堆区
堆区分为两大块:一个是Old区,一个是Young区
Young区分为两大块:一个是Survivor区(S0+S1),一块是Eden区
S0和S1一样大,也可以叫From和To
也可以使用java自带的jvisual工具看一下堆和非堆(在java目录的bin下面)
方法区
jdk7或者之前叫 perm space 永久代
jdk8或者以后叫 meta space 元空间
 
0

1.设计理解

这里先不讨论什么具体的GC这个放在下面一个章节具体讲解
下面会一步步理解为什么要分young区 和使用 eden区 和Survivor区

1.1.过程理解

1.1.1.无young区

在没young区的时候
0
空间很大回收起来比较耗时,其实绝大部分对象的生命周期都很短不需要长期停留在内存空间
那如果垃圾回收之后 那么内存空间存在空间碎片fragmented造成空间浪费,有比较大的对象 就无法存入
0
这个时候Garbage Collector会觉得应该是空间不够了所以启动垃圾收集线程 gc thread 一定会抢夺cpu的时间片会和业务代码抢夺cpu资源

1.1.2.引入young区

0
这样引入young区之后 不必要扫描全部的堆内存 只需要扫描young区就可以 这也节省了性能的消耗
那么young区什么时候到老年代,他会有一个阈值的判断这个后面在说
这样引入了young区之后 还是有会空间碎片的情况产生,那么怎么办?

1.1.2.引入eden、Survivor区

0
这样换取了eden区的空间连续性 但是再次发生young gc的时候
他会对整个young区进行垃圾回收 这样的话Survivor区又会有空间碎片的情况发生
Eden区和Survivor区哪个大?
肯定是Eden区,因为每次创建对象还是在Eden区产生,这些都是一些朝生夕死的对象很快就会被回收
Survivor区还是会发生空间碎片的情况产生。那么怎么办?

1.1.2.1.拆分Survivor区

0
再打开jvisualvm看一下是不是一样的
0
那么问题来了?那age到什么时候去老年代?
1.他会有一个阈值的判断 到达一定的阈值就会去老年代(因为对象头的age只有4位那么撑死了就是15岁)
2.如果s0或者s1空间不够用了 就会向old区借空间 (空间担保机制)
3.大文件 young区放不下。他会直接去老年代

1.2.结论流程图

0

2.代码演示

创建一个springboot的demo 启动项目观察jvm内存模型
0

2.1.堆

限制一下堆内存大小-Xmx50M -Xms50M
@RestController
public class HeapController {
    List<Worker> list=new ArrayList<Worker>();
    @GetMapping("/heap")
    public String heap() throws Exception{
        while(true){
            list.add(new Worker());
}}}

打开打开工具看看过程 最后控制台肯定会报OOM

2.2.方法区

限制一下大小 -XX:MetaspaceSize=50M -XX:MaxMetaspaceSize=50M
自己创建类很慢 我可以用一下asm写好的方法 引入一下他的依赖
<dependency>
    <groupId>asm</groupId>
    <artifactId>asm</artifactId>
    <version>3.3.1</version>
</dependency>

  工具类

public class MetaspaceUtil extends ClassLoader {

    public static List<Class<?>> createClasses() {
        List<Class<?>> classes = new ArrayList<Class<?>>();
        for (int i = 0; i < 10000000; ++i) {
            ClassWriter cw = new ClassWriter(0);
            cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "Class" + i, null,
                    "java/lang/Object", null);
            MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>",
                    "()V", null, null);
            mw.visitVarInsn(Opcodes.ALOAD, 0);
            mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object",
                    "<init>", "()V");
            mw.visitInsn(Opcodes.RETURN);
            mw.visitMaxs(1, 1);
            mw.visitEnd();
            MetaspaceUtil test = new MetaspaceUtil();
            byte[] code = cw.toByteArray();
            Class<?> exampleClass = test.defineClass("Class" + i, code, 0, code.length);
            classes.add(exampleClass);
        }
        return classes;
    }

  

入口
@RestController
public class NonHeapController {

    List<Class<?>> list=new ArrayList<Class<?>>();
    @GetMapping("/nonheap")
    public String heap(){
        while(true){
            list.addAll(MetaspaceUtil.createClasses());
        }
    }
}

  

最后也会oom
方法区的oom在控制台会看不到溢出的情况
因为:
java7 perm space空间是在java内存中
java8 metaspac空间用的是本地内容 系统的内存

 

posted @ 2022-05-14 17:45  Nuti  阅读(39)  评论(0编辑  收藏  举报