详解JVM系列之内存模型

 更新时间:2021年06月10日 10:50:33   作者:smileNicky  
JVM是一种用于计算设备的规范,它是一个虚构出来的机器,是通过在实际的计算机上仿真模拟各种功能实现的。JVM的内存区域可以被分为:线程、栈、堆、静态方法区。本文将介绍JVM的内存模型,感兴趣的小伙伴,可以参考下

1. 内存模型和运行时数据区

这一章学习java虚拟机内存模型(Java Virtual machine menory model),可以这样理解,jvm运行时数据库是一种规范,而JVM内存模型是对改规范的实现

java虚拟机重点存储数据的是堆和方法区,所以本章节也重点从这两个方面进行比较详细描述。堆和方法区是内存共享的,而java虚拟机栈、Native方法栈、程序计数器是线程私有的

2、思维导图和图例

一个是非堆区(方法区),方法区也一般被称之为“永久代”。另外一个是堆区,分为young区和old区,young区又分为两个部分,一个是Eden区,一个是Survivor区(S0+S1),S0区也可以称之From区,S1也可以称之为To区

3、对象向JVM申请空间

4、为什么需要Survivor区?

为什么需要Survivor区?只有Eden不行吗?

假设不设计出Survivor区,Eden区进行一次MinorGC,对象就直接被送到Old区,这样一来Old区很快就被填满,Old区一满,就会进行FullGC(Old区会进行MajorGC,一般伴随着MinorGC),FullGC是很耗时的,所以设计出Survivor区的目的是减少对象被送到Old区,有一个过渡的Survivor区

补充:Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代
Eden:S1:S2是8:1:1

5、为什么需要两个Survivor区?

需要两个Survivor区的目的是为了避免内存碎片化。为什么这么说?
假设只设计出一个Survivor区,一旦Eden区满了,就会进行Minor GC,Eden区存活的对象就会被移动到Survivor区,等下一次Eden区满时候,问题就来了,进行MinorGC就将Eden区对象硬放到Survivor区,这样就导致了对象所占的内存是不连续的

6、例子进行验证

堆内存溢出

import lombok.Data;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class HeapController {

    List<Foo> list = new ArrayList<Foo>();
    @GetMapping(value = {"heap"})
    public String heapTest() {
        while (true) {
            list.add(new Foo());
        }
    }


    @Data
    class Foo {
        String str;
    }
}

访问接口,出现内存溢出;

java.lang.OutOfMemoryError: Java heap space

...

可以设置参数:比如-Xms64M -Xmx512M

方法区内存溢出

使用asm,maven配置:

<dependency>
  <groupId>asm</groupId>
  <artifactId>asm</artifactId>
  <version>3.3.1</version>
</dependency>

编写代码,向方法区中添加Class的信息,注意,电脑性能不够好,不要执行此代码,很容易,造成电脑重启,太吃内存,也可以调小循环次数

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

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

public class MyMetaspace 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();
      MyMetaspace test = new MyMetaspace();
      byte[] code = cw.toByteArray();
      Class<?> exampleClass = test.defineClass("Class" + i, code, 0,
              code.length);
      classes.add(exampleClass);
    }
    return classes;
  }
}

方法区测试接口:

import com.example.jvm.jvmexceptionexample.asm.MyMetaspace;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

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

@RestController
public class NonHeapController {

    List<Class<?>> list = new ArrayList<Class<?>>();

    @GetMapping(value = {"/noheap"})
    public String noheap() {
        while (true) {
            list.addAll(MyMetaspace.createClasses());
        }
    }

}

java.lang.OutOfMemoryError: Metaspace

at java.lang.ClassLoader.defineClass1(Native Method) ~[na:1.8.5_54]

处理方法,设置Metaspace的大小,比如-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=512M

Java虚拟机栈

在前面学习,java虚拟机栈是通过栈帧方式存储,一个方法对应一个栈帧,按照队列模式进栈,所以要测试程序导致java虚拟机栈出现问题,可以通过递归方法方式进行测试:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class StackController {

    public static long count = 0;

    public static void add(long i) {
        count ++ ;
        add(i);
    }

    @GetMapping(value = {"stack"})
    public void stack() {
        add(1);
    }

}

StackOverflow,栈溢出异常:

java.lang.StackOverflowError: null

at com.example.jvm.jvmexceptionexample.controller.StackController.add(StackController.java:14) ~[classes/:na]

处理方法,设置-Xss256k:设置每个线程的堆栈大小。JDK 5以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K

以上就是详解JVM系列之内存模型的详细内容,更多关于JVM 内存模型 内存结构的资料请关注脚本之家其它相关文章!

相关文章

  • js实现页面多个日期时间倒计时效果

    js实现页面多个日期时间倒计时效果

    这篇文章主要为大家详细介绍了js实现页面多个日期时间倒计时效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-06-06
  • javascript:void(0)的问题使用探讨

    javascript:void(0)的问题使用探讨

    想做一个链接点击后不做任何事情,方法有很多,不过不利于用户体验,javascript:void(0)可以有效解决这个问题
    2014-04-04
  • js回文数的4种判断方法示例

    js回文数的4种判断方法示例

    这篇文章主要给大家介绍了关于js回文数的4种判断方法,文中通过示例代码介绍的非常详细,对大家学习或者使用js具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • 基于Three.js插件制作360度全景图

    基于Three.js插件制作360度全景图

    这篇文章主要介绍了基于Three.js插件制作的360度全景图,代码简单易懂,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-11-11
  • JavaScript异步队列进行try catch时的问题解决

    JavaScript异步队列进行try catch时的问题解决

    这篇文章主要介绍了JavaScript异步队列进行try catch时的问题解决,文章围绕主题展开详细的的相关内容介绍,需要的朋友可以参考一下
    2022-07-07
  • JavaScript学习笔记之DOM基础操作实例小结

    JavaScript学习笔记之DOM基础操作实例小结

    这篇文章主要介绍了JavaScript学习笔记之DOM基础操作,结合实例形式总结分析了javascript针对dom元素节点、属性的相关获取、设置等操作技巧,需要的朋友可以参考下
    2019-01-01
  • 清除浏览器缓存的几种方法总结(必看)

    清除浏览器缓存的几种方法总结(必看)

    下面小编就为大家带来一篇清除浏览器缓存的几种方法总结(必看)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-12-12
  • Bootstrap标签页(Tab)插件切换echarts不显示问题的解决

    Bootstrap标签页(Tab)插件切换echarts不显示问题的解决

    这篇文章主要给大家介绍了关于Bootstrap标签页(Tab)插件切换echarts不显示问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-07-07
  • JavaScript动态操作select下拉框

    JavaScript动态操作select下拉框

    这篇文章介绍了JavaScript动态操作select下拉框的方法,文中通过示例代码介绍的非常详细。对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • JavaScript 变量,数据类型基础实例详解【变量、字符串、数组、对象等】

    JavaScript 变量,数据类型基础实例详解【变量、字符串、数组、对象等】

    这篇文章主要介绍了JavaScript 变量,数据类型基础,结合实例形式详细分析了JavaScript变量声明、字符串、数组、对象等基本使用方法与操作注意事项,需要的朋友可以参考下
    2020-01-01

最新评论