jvm内存结构及运行原理(详解JVM系列之内存模型)
jvm内存结构及运行原理
详解JVM系列之内存模型目录
- 1. 内存模型和运行时数据区
- 2、思维导图和图例
- 3、对象向JVM申请空间
- 4、为什么需要Survivor区?
- 5、为什么需要两个Survivor区?
- 6、例子进行验证
- 堆内存溢出
- 方法区内存溢出
- Java虚拟机栈
这一章学习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区
5、为什么需要两个Survivor区?补充:Minor GC:新生代
Major GC:老年代
Full GC:新生代+老年代
Eden:S1:S2是8:1:1
6、例子进行验证需要两个Survivor区的目的是为了避免内存碎片化。为什么这么说?
假设只设计出一个Survivor区,一旦Eden区满了,就会进行Minor GC,Eden区存活的对象就会被移动到Survivor区,等下一次Eden区满时候,问题就来了,进行MinorGC就将Eden区对象硬放到Survivor区,这样就导致了对象所占的内存是不连续的
堆内存溢出
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 内存模型 内存结构的资料请关注开心学习网其它相关文章!
- jvm运动数据区总结(JVM上高性能数据格式库包Apache Arrow入门和架构详解Gkatziouras)
- jvm内存结构及运行原理(详解JVM系列之内存模型)
- 如何设置tomcat的jvm(Tomcatc3p0配置jnid数据源2种实现方法解析)
- tomcat优化jvm(Tomcat修正JDK原生线程池bug的实现原理)
- docker可用容量查看(docker 查看jvm内存占用方式)
- 网友很惭愧,自己写了很多年的字,到头来还不如一名小学生写的好(自己写了很多年的字)
- 中华第一楷 张瑞龄 86岁高龄,苦练楷书71年,一幅字卖593万(中华第一楷张瑞龄)
- 冯骥才 年意(冯骥才年意)
- ()
- 百事大吉蓝底 绿底手机高清壁纸(绿底手机高清壁纸)
- 蓝底证件照怎么制作 证件照换底色 换尺寸快速搞定(蓝底证件照怎么制作)
热门推荐
- 云服务硬盘挂载有几种方法(腾讯云 阿里云 挂载硬盘方法数据盘)
- python调用支付宝支付接口(python实现支付宝转账接口)
- 阿里云rds需要读写分离吗(为什么使用阿里云RDS?使用RDS有什么好处?)
- 宝塔面板一键部署教程(使用宝塔面板负载均衡插件的心得和解决的方法)
- opencv调用摄像头图像识别(Python+OpenCV采集本地摄像头的视频)
- docker插件容器使用(Docker容器开jupyter不能访问到的解决方法)
- python批量注册(python实现批量注册网站用户的示例)
- photoshop常用快捷键
- ASP.NET全角与半角相互转换
- vue 设置头部可以左右滑动(vue实现左右滑动效果实例代码)